I used this code in my .htaccess file and it is working great to prevent hotlinking:
RewriteEngine On
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##https?://\1/.*
RewriteRule \.(gif|jpg|jpeg|png|tif|pdf|wav|wmv|wma|avi|mov|mp4|m4v|mp3|zip?)$ - [F]
Now I want to allow one of my subdomains (cdn.example.com) to access my files using a GET request.
It is not possible to add http_reffer to my GET request! I should only handle it with .htaccess.
How should I add my subdomain as an exception in this code?
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##https?://\1/.*
Use the following condition instead to allow for an optional cdn subdomain:
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##https?://(cdn\.)?\1/
(The trailing .* is not required.)
UPDATE#1:
It is not possible to add http_reffer to my GET request!
Not sure exactly what you mean by this, but if the Referer header is not being sent with these requests for some reason (perhaps you have a restrictive Referrer-Policy?) then you will likely need to allow an empty referer.
To allow an empty Referer, add the following as the first condition:
RewriteCond %{HTTP_REFERER} !^$
:
However, this will also allow direct requests. But due to the unreliable nature of the Referer header you really need to allow an empty Referer header anyway since some legitimate users might be suppressing it.
Aside: These directives to prevent hotlinking also block search engines - if that is a concern?
UPDATE#2:
i have another server for cdn.mydomain.com . and on that server i have a php script that will convert images to specific format . i should allow this script to read images from main domain
:
i can't edit that php script to change request method
Ideally, the script would be sending a custom HTTP request header indicating that the request is coming from your "CDN". You could then check for this in the above directive to allow the request.
If all these requests are coming directly from this other server then you can perhaps allow all requests from this server - identified by the server's IP address.
For example, if 203.0.113.111 is your server's IP address, then:
RewriteEngine On
RewriteCond %{REMOTE_ADDR} !=203.0.113.111
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##https?://\1/
RewriteRule \.(gif|jpg|jpeg|png|tif|pdf|wav|wmv|wma|avi|mov|mp4|m4v|mp3|zip?)$ - [F]
Related
We have a htaccess rule like this:
RewriteRule ^(.*)/(.*)/(.*) ../app$1/scripts/api/index.php?fn=$2&$3 [L]
This works fine in most cases, however, Apache decodes the url before it arrives at this rule, so a url like beta/list/&cat=red%20%26%20blue, is seen by htaccess as beta/list/&cat=red & blue so we get cat='red' and blue=null coming into index.php instead of cat='red & blue'.
I've read that the workaround for this issue is to use server variables like %{REQUEST_URI} %{THE_REQUEST} in the htaccess rule as these are not decoded before use, but it's difficult to implement. The question mark in the RewriteRule makes everything go crazy and I can't figure out how to escape it.
Can any experts out there help me fix the rule below to behave like the one above?
RewriteCond %{REQUEST_URI} ^(.*)/(.*)/(.*)
RewriteRule . ../app%1/scripts/api/index.php?fn=%2&%3 [L]
Indeed, the solution is to use the special server-variable called THE_REQUEST.
From mod_rewrite documentation:
THE_REQUEST
The full HTTP request line sent by the browser to the server (e.g.,
"GET /index.html HTTP/1.1"). This does not include any additional
headers sent by the browser. This value has not been unescaped
(decoded), unlike most other variables below.
Here is how your rule should look like
# don't touch urls ending by index.php
RewriteRule index\.php$ - [L]
# user request matching /xxx/xxx/xxx (with optional query string)
RewriteCond %{THE_REQUEST} \s/([^/\?]+)/([^/\?]+)/([^\?]+)(?:\s|\?) [NC]
RewriteRule ^ ../app%1/scripts/api/index.php?fn=%2&%3 [L,QSA]
Please note that you shouldn't be using relative path for internal rewrite, which could lead to confusion. Instead, define a RewriteBase, use an absolute path or start from the domain root with a /.
UPDATE
Since you can have encoded forward slashes in your url, you need to set AllowEncodedSlashes to NoDecode (or On but it's unsafe). Note also that, due to a bug, you must put this directive inside a virtual host context, even if the server config context is said to be OK (otherwise, it is simply ignored). By default, AllowEncodedSlashes is set to Off. So, Apache handles encoded slashes automatically by itself and refuses them, without passing the request to mod_rewrite. See the official documentation here.
I have a case where I want to protect some files (listing the directory AND accessing the content) unless a specific header is present and if that header contains the correct value.
For example if a header named "x-test-header" is present AND has a value of "abc123" then let the traffic go through, otherwise return a 403.
I have tried a variety of things such as:
RewriteEngine On
RewriteCond %{HTTP_x-test-header} !^abc123
RewriteRule ^.*$ - [R=403,L]
The above works in the sense that it blocks traffic but when I use a REST client to include the header it still returns a 403. Clearly I am not doing something correctly, can anyone point me in the right direction?
Using Apache 2.4.33 on AWS.
This rule should work for you:
RewriteCond %{HTTP:x-test-header} !^abc123$ [NC]
RewriteRule ^ - [F]
RewriteEngine on
RewriteCond %{QUERY_STRING} (^|&)public_url=([^&]+)($|&)
RewriteRule ^process\.php$ /api/%2/? [L,R=301]
Where domain.tld/app/process.php?public_url=abcd1234 is the actual location of the script.
But I am trying to get .htaccess to make the URL like this: domain.tld/app/api/acbd1234.
Essentially hides the process.php script and the get query ?public_url.
However the script above is returning error 404 not found.
I think this is what you are actually looking for:
RewriteEngine on
RewriteCond %{QUERY_STRING} (?:^|&)public_url=([^&]+)(?:$|&)
RewriteRule ^/?app/process\.php$ /app/api/%1 [R=301,QSD]
RewriteRule ^/?app/api/([^/]+)/?$ /app/process.php?public_url=$1 [END]
If you receive an internal server error (http status 500) for that then check your http servers error log file. Chances are that you operate a very old version of the apache http server, you may have to replace the [END] flag with the [L] flag which probably will work just fine in this scenario.
And a general hint: you should always prefer to place such rules inside the http servers (virtual) host configuration instead of using dynamic configuration files (.htaccess style files). Those files are notoriously error prone, hard to debug and they really slow down the server. They are only supported as a last option for situations where you do not have control over the host configuration (read: really cheap hosting service providers) or if you have an application that relies on writing its own rewrite rules (which is an obvious security nightmare).
UPDATE:
Based on your many questions in the comments below (we see again how important it is to be precise in the question itself ;-) ) I add this variant implementing a different handling of path components:
RewriteEngine on
RewriteCond %{QUERY_STRING} (?:^|&)public_url=([^&]+)(?:$|&)
RewriteRule ^/?app/process\.php$ /api/%1 [R=301,QSD]
RewriteRule ^/?api/([^/]+)/?$ /app/process.php?public_url=$1 [END]
I am trying to get .htaccess to make the URL like this: example.com/app/api/acbd1234.
You don't do this in .htaccess. You change the URL in your application and then rewrite the new URL to the actual/old URL. (You only need to redirect this, if the old URLs have been indexed by search engines - but you need to watch for redirect loops.)
So, change the URL in your application to /app/api/acbd1234 and then rewrite this in .htaccess (which I assume in in your /app subdirectory). For example:
RewriteEngine On
# Rewrite new URL back to old
RewriteRule ^api/([^/]+)$ process.php?public_url=$1 [L]
You included a trailing slash in your earlier directive, but you omitted this in your example URL, so I've omitted it here also.
If you then need to also redirect the old URL for the sake of SEO, then you can implement a redirect before the internal rewrite:
RewriteEngine On
# Redirect old URL to new (if request by search engines or external links)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} (?:^|&)public_url=([^&]+)(?:$|&)
RewriteRule ^process\.php$ /app/api/%1? [R=302,L]
# Rewrite new URL back to old
RewriteRule ^api/([^/]+)$ process.php?public_url=$1 [L]
The check against REDIRECT_STATUS is to avoid a rewrite loop. ?: inside the parenthesised subpattern avoids the group being captured as a backreference.
Change the 302 (temporary) to 301 (permanent) only when you are sure it's working OK, to avoid erroneous redirects being cached by the browser.
I have an Apache web-server that acts as a web front-end for iPhone and iPad applications that communicate by POST and JSON only.
Is there any way to prevent Apache from answering requests that are invalid? I can see my error log is filled with attempts to open files such as /admin.php /index.php etc - files that don't exist on my server.
I believe this is possible with IIS, but can you do the same thing with Apache?
Basically I want the request to appear timed out unless you post exactly the right content to the right file - or at least if you do not request an existing file. This would make the server appear non-existing to everyone but my iPhone users as all communication is SSL and directories are not really guess-able.
I did disable the ServerTokens and all that, but I still get File not found etc. when I access the server requesting a random file, which is what these bots do constantly.
You can limit what HTTP methods Apache responds to by using mod_rewrite to redirect or deny unwanted requests.
Note that JSON is not an HTTP method. GET, POST, PROPFIND etc are HTTP method. For a full list of HTTP methods please see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
If your application truly does not need to honor GET requests, then you can block all other methods.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} !^(POST) [NC]
RewriteRule ^(.*)$ - [F,L]
</IfModule>
I've not tested this but it should block anything other than a POST method. You can add multiple methods.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} !^(POST|GET) [NC]
RewriteRule ^(.*)$ - [F,L]
</IfModule>
Would permit only POST and GET.
You may need to also specify 'Options FollowSymLinks` on some servers due to how rewrites are handled.
If you must permit GET requests, then you will see those not found errors. There is nothing that prevents someone from issuing a HTTP request for an object that does not exist.
I am hosting a couple of domains of the same wordpress installation, now I'd like to have a per-domain folder for some various files I need to put up there.
Essentially I want to map like this:
URL Path
webbfarbror.se/f/* _files/webbfarbror.se/*
grapefrukt.com/f/* _files/grapefrukt.com/*
This little snippet does the job nicely and the RewriteCond let's me enable and disable this on a per domain basis.
ReWriteCond %{HTTP_HOST} webbfarbror.se
ReWriteRule ^f/(.*)$ _files/%{HTTP_HOST}/$1 [L]
However, a file at say, http://grapefrukt.com/f/awesome.jpg is also accessible at it's "real" URL http://grapefrukt.com/_files/grapefrukt.com/awesome.jpg
All my attempts result in infinite redirects back and forth.
How do I disable access through the latter URL?
You can examine the original request as it was sent to the server, which is available as %{THE_REQUEST}. Checking for the /_files/ prefix indicates that the request was of the latter type, and you can then redirect to the appropriate format:
RewriteCond %{THE_REQUEST} ^[A-Z]+\s/_files/
RewriteRule ^_files/[^/]+/(.*)$ http://%{HTTP_HOST}/f/$1 [R=301,L]