With many recursive algorithms, it is not uncommon to actually implement them with two procedures: one to set up the initial conditions and a second one to do the actual recursion, like so:
(define (rid-inner li)
(cond
[(empty? li) '()]
[(empty? (rest li)) '()]
[(cons (first li) (rid-inner (rest li)))]))
(define (rid1 L)
(define r (if (empty? L) '() (rest L)))
(rid-inner r))
With (define r (if (empty? L) '() (rest L))) we strip off the first element of the list; no recursion is actually necessary for this step. Then we define the same procedure you had before with a different name and call it with our new list that already has the first element stripped off. If you want the first element stripped off, just strip off the first element; don't overthink it :) .
In a language like Racket that allows closures and nested procedures, you don't actually even need to define both procedures at the top "global" module scope; just define your recursive procedure inside your initial procedure and call it from there. Example:
(define (rid2 L)
(define r (if (empty? L) '() (rest L)))
(define (rid-inner li)
(cond
[(empty? li) '()]
[(empty? (rest li)) '()]
[(cons (first li) (rid-inner (rest li)))]))
(rid-inner r))
Another, somewhat cleaner, way to do the above is to use a named let, which allows us to simultaneously set up our initial conditions, create a named procedure, and then call that procedure immediately from within itself. We do that like so:
(define (rid3 L)
(let rid-inner ([li (if (empty? L) '() (rest L))])
(cond
[(empty? li) '()]
[(empty? (rest li)) '()]
[(cons (first li) (rid-inner (rest li)))])))
To those unfamiliar with Racket, Scheme, or a related Lisp, the named let in rid3 may be more cryptic at first since it is really doing two or three things at once. You can find the docs for it here. Don't be fooled though, it works exactly the same as rid2. Named let exists precisely because this pattern is so common.