Search code examples
reactjs.htaccesshttp-redirectmod-rewriteurl-rewriting

NextJS htaccess setup without a trailing slash on URLs


Found a problem with my site on NextJS. During development, I navigated the site using buttons and manually changing the browser address bar. It happened that I accidentally added a slash to the end, but my localhost server removed it and everything worked fine.

But everything changed when I uploaded my static application to the hosting. It automatically began to add these slashes when reloading the page. Because of this, my pictures on the site break.

As far as I understand, you need to correctly configure the .htaccess file.

Here is what it looks like now:

RewriteEngine On
RewriteRule ^([^/]+)/$ $1.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]+)/$ $1.html
RewriteRule ^([^/]+)/([^/]+)/$ /$1/$2.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)$ /$1/ [R=301,L]

Solution

  • RewriteRule ^([^/]+)/$ $1.html
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^([^/]+)/$ $1.html
    RewriteRule ^([^/]+)/([^/]+)/$ /$1/$2.html
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
    RewriteRule (.*)$ /$1/ [R=301,L]
    

    Your existing rules are all expecting (or forcing) a trailing slash on all your URLs. So, if the canonical URL (and the URL you are linking to) does not include a trailing slash then all these rules essentially need to be reversed. However, there are other issues here (the first rule, for instance, is unconditionally rewriting the request to append the .html extension, which is repeated in the next rule with a condition.)

    Try the following instead:

    RewriteEngine On
    
    # (OPTIONAL) Remove trailing slash if it happens to be on the request
    # Exclude physical directories (which must end in a slash)
    RewriteRule %{REQUEST_FILENAME} !-d
    RewriteRule (.+)/$ /$1 [R=301,L]
    
    # Rewrite request to corresponding ".html" file if it exists
    RewriteCond %{DOCUMENT_ROOT}/$1.html -f
    RewriteRule ^([^.]+)$ $1.html [L]
    

    Your original directives only handled URLs with one or two path depth (eg. /foo/ or /foo/bar/). The second rule above handles any path depth (if so required). eg. /foo, /foo/bar, /foo/bar/baz etc. (no trailing slash).

    As an optimisation I've assumed your URLs that require rewriting do not contain dots (that are otherwise used to delimit the file extension).

    Note that the RewriteRule pattern (first argument) matches against the URL-path only (not the query string). If there is any query string on the initial request then this is simply passed through by default. (With regards to the rewrite and client-side JS, the query string is available on the initial request and should be parsed as before.)

    Because of this, my pictures on the site break.

    This will happen if you are using relative URLs to your images. You should really be using root-relative (starting with a slash) or absolute URLs to resolve this issue. See also: