Home > Enterprise >  htaccess - Force a slash to the end of a dynamic URL
htaccess - Force a slash to the end of a dynamic URL

Time:12-15

I have a single index.php file in a /slug subdirectory and would like to load dynamic content based on the file path. Regardless of what the url is, the content should reference that index.php.

In my code below, the slash is not being added at the end of the url. For example, example.com/slug/33 should be displayed in the address bar as example.com/slug/33/.

I have the following .htaccess in /slug:

Options -Indexes

# Turn mod_rewrite on
RewriteEngine On
RewriteBase /

# Dynamic url
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /slug/index.php/?path=$1 [NC,L,QSA]

I tried adding a / between index.php and ?path=$ but I'm not getting the desired result. Is this even possible?

CodePudding user response:

RewriteRule ^(.*)$ /slug/index.php/?path=$1 [NC,L,QSA]

Changing the substitution string here changes the target of your internal rewrite - it does nothing to change the visible URL. By adding a slash after index.php you are (unnecessarily) adding additional pathname information (path-info) to the resulting URL that your application receives.

To change the visible URL (to append the slash) you need to implement an external redirect. However, to confirm... you must already be linking to the correct canonical URL (ie. with a slash) in your internal links. Appending the slash to the URL in .htaccess is only if you have changed the URL and search engines or 3rd parties are still using the old non-canonical URL (without a trailing slash).

Since the .htaccess file is in the /slug subdirectory and you are rewriting to index.php in that subdirectory then you don't need to prefix the rewritten URL with /slug/. By default, a relative URL-path is relative to the directory that contains the .htaccess file. However, you must also remove the RewriteBase directive (or set this "correctly" to RewriteBase /slug).

To redirect to append a trailing slash you can add the following before the current rewrite:

# Append trailing slash if omitted
RewriteRule ^(.*(?:^|/)[^/.] )$ /slug/$1/ [R=301,L]

This requires the /slug/ prefix on the substitution string (unless RewriteBase /slug is set), otherwise the external redirect will attempt to redirect to a file-path, which will "break".

The RewriteRule pattern ^(.*(?:^|/)[^/.] )$ captures URL-paths that do not already end in a slash and do not contain a dot in the last path segment. This is to avoid matching URLs that already contain (what looks-like) a file extension, ie. your static resources (images, CSS, JS, etc.). This should avoid the need for a filesystem check (which are relatively expensive) - to check that the request does not already map to a file. Although, if you are not referencing any static resources with the /slug/ prefix in the URL then this can be simplified.

NB: You should first test with a 302 (temporary) redirect to avoid potential caching issues.

In context (with the use of RewriteBase):

Options -Indexes

# Turn mod_rewrite on
RewriteEngine On
RewriteBase /slug

# Append trailing slash if omitted
RewriteRule ^(.*(?:^|/)[^/.] )$ $1/ [R=301,L]

# Dynamic url
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (. ) index.php?path=$1 [QSA,L]

The use of RewriteBase avoids you having to specify /slug/ in the other directives.

In the regex ^(.*)$, the start-of-string (^) and end-of-string ($) anchors are superfluous. And you might as well change this to use the quantifier, since you don't want to match the base directory anyway (saves two additional filesystem checks). The NC flag was also superfluous here.

  • Related