3

I have a mod_rewrite to allow example.com/location as example.com/?page=location

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} !^page
RewriteRule ^(.*)/?$ /?page=$1 [L]

But how do I ignore certain files or all folders, so my example.com/styles.css and example.com/scripts.js are not changed to example.com/?page=styles.css and example.com/?page=scripts.js

Ignore all files with an ending .css, .js, .php, .html, .png, .* etc

6

Instead of making exceptions for a growing list of expected file types, it is more usual (and more flexible) to just make an exception if the file exists (or, optionally, is a directory). For example:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{QUERY_STRING} !^page
RewriteRule ^(.*?)/?$ index.php?page=$1 [L]

The above says... if the request is not a file or directory on the filesystem then proceed and rewrite the request. The request should be rewritten directly to the file that handles the request, eg. index.php. In your example /?page=$1 is reliant on mod_dir issuing an additional subrequest for the DirectoryIndex, ie. index.php.

Since all your CSS, JS, images, etc. are all physical files on the filesystem then they won't be rewritten.

Note that the regex ^(.*)/?$, as stated in the question, is arguably incorrect. It looks like you are trying to omit an optional trailing slash from the captured subpattern. So you could request either /location or /location/, but only capture location in both cases. However, that is not what this regex does. Because the preceding subpattern/quantifier (.*) is greedy, this will also consume the optional trailing slash, so the /? that follows doesn't actually do anything. The regex ^(.*)/?$ is the same as ^(.*)$ (or simply (.*)). If you wanted to exclude an optional trailing slash from being captured, you would need to make the preceding pattern non-greedy, so that it doesn't consume the optional trailing slash. eg. ^(.*?)/?$ (as stated above). Or, use alternation, eg. ^(.*)(?:/|)$. Or, make the preceding pattern more restrictive, so it won't match a slash. eg. ^([a-z]*)/?$. But note that making the trailing slash optional here, potentially creates a duplicate content issue.

Apache even provides a one-liner (part of mod_dir) that essentially does the same thing:

FallbackResource /index.php

This internally rewrites all requests for non-existent files to /index.php. However, you would then need to check the requested URL (eg. $_SERVER['REQUEST_URI'] in PHP), rather than the page URL param in order to route the request.

Reference:
https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource


UPDATE: Ignore all files with an ending .css, .js, .php, .html, .png

However, having said that, if you want to ignore specific file extensions, it will be more efficient to first exclude these known file types before the general filesystem check (as mentioned above). The filesystem check is relatively expensive when compared to a regex pattern match.

So, combining these two checks, you could do something like the following:

RewriteCond %{REQUEST_URI} !\.(css|js|php|html|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{QUERY_STRING} !^page
RewriteRule ^(.*?)/?$ index.php?page=$1 [L]

Or, perform the file extension check in the RewriteRule pattern (slightly more efficient - since this is processed first). For example:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{QUERY_STRING} !^page
RewriteCond %{REQUEST_URI} ^/(.*?)/?$
RewriteRule !\.(css|js|php|html|png)$ index.php?page=%1 [NC,L]

The last RewriteCond directive is then used to grab the relevant part of the URL-path (from the example in the question), which is referenced using the %1 (as opposed to $1) backreference in the RewriteRule substitution. (Whether you need to do this will depend on your URL structure and what your script expects.)

Depending on your URL structure, it may be enough to simply exclude URLs that have a file extension, without being specific as to the actual file extensions. In which case you can probably omit the file-check (unless you have physical files without file extensions - unlikely). For example:

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{QUERY_STRING} !^page
RewriteCond %{REQUEST_URI} ^/(.*?)/?$
RewriteRule !\.[a-z0-9]{2,4}$ index.php?page=%1 [NC,L]

In the above example, a file extension is assumed to be a dot followed by 2, 3 or 4 alphanumeric characters, which covers .css, .js, .mp4, etc.

3
  • how can I use 3 variables like page/method/id?
    – themhz
    Feb 12 at 19:37
  • 1
    RewriteRule ^(\w+)/(\w+)/(\w+)$ index.php?page=$1&method=$2&id=$3 [L] @themhz
    – MrWhite
    Feb 12 at 23:59
  • thank you I fixed it
    – themhz
    Feb 14 at 17:05
4

This should work:

RewriteEngine On
RewriteCond %{REQUEST_URI} !\.png$ [NC]
RewriteCond %{REQUEST_URI} !\.html$ [NC]
RewriteCond %{REQUEST_URI} !\.php$ [NC]
RewriteCond %{REQUEST_URI} !\.css$ [NC]
RewriteCond %{REQUEST_URI} !\.jpg$ [NC]
RewriteCond %{QUERY_STRING} !^page
RewriteRule ^(.*)/?$ /something.php?page=$1 [L]

It scans the URL for each extension one by one regardless of the casing one uses. For example, your script won't execute if someone requested something with a .jpg or even .JpG extension.

Also, I recommend putting in the script file name in for the rewrite rule otherwise extra processing will be done in the background by apache because it has to figure out what default index file to use to insert between /? in /?page=$1.

2
  • Thank you, works perfectly and I understand a little bit more of .htaccess!
    – Zed
    Dec 14 '15 at 21:26
  • And don't forget to backslash-escape the literal dot in the regex (that delimits the file extension), otherwise, it matches any character - which could potentially match a legitimate page URL.
    – MrWhite
    Jun 19 '20 at 1:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.