Proper way of deallocating memory in SLL?
Answers to your questions

Am I causing any memory leaks, and is there a better, safer way to do things?

Yes, see for example your second question. A safer way is to use something like std::unique_ptr to manage pointers for you.

In remove_front(), should I set head to nullptr, or should I delete it instead?

You should delete it of course. If you just set head to nullptr, the memory for the head node is still allocated.
Avoid using names that start with an underscore
Names that start with underscores are reserved for the standard library. There are some more specific rules, but in general it is best to avoid them completely. You can safely append an underscore to the end of a name though.
On the other hand, while you have _length, head is written without an underscore. If you are not consistent in marking private variables with an underscore, there is not much point to it at all.
Flawed logic in remove(int index)
There is an issue with the for-loop in remove(int index): you are moving the head pointer instead of node_ptr. In fact you are not using node_ptr at all after initializing it. The compiler should have given you a warning about this (be sure to enable compiler warnings and fix them).
To make this work correctly, even when removing the head node, you should use an extra layer of redirection: you want a pointer to the pointer you need to update, like so:
Node **node_ptr = &amp;head;           // node_ptr is pointing to the variable `head`
for (int a = 0; a &lt; index - 1; ++a)
    node_ptr = &amp;(*node_ptr)-&gt;next; // node_ptr is updated to point to some node's `next` variable

Node* temp = *node_ptr;            // get the actual Node pointer to
*node_ptr = (*node_ptr)-&gt;next;     // update `head` or a `next` to skip one Node
delete temp;                       // delete the target Node

Once you grasp this you've become a two-star programmer!
Unnecessary code duplication
You have three different functions for removing an element, but they all do mostly the same. In fact, the first one, remove(int index), does everything you need. You don't need to treat index == 0 as a special case, the code will work correctly anyway. You can make remove_front() and remove_back() call the generic remove() function:
void remove(int index)
{
    if (index &lt; 0 || index &gt;= _length)
        throw &quot;index is out of range&quot;;

    Node **node_ptr = &amp;head;
    for (int a = 0; a &lt; index - 1; ++a)
        node_ptr = &amp;(*node_ptr)-&gt;next;

    Node* temp = *node_ptr;
    *node_ptr = (*node_ptr)-&gt;next;
    delete temp;

    _length -= 1;
}

void remove_front()
{
    remove(0);
}

void remove_back()
{
    remove(_length - 1);
}