mod-rewrite redirect but prevent direct access

北慕城南 提交于 2019-12-30 09:59:53

问题


I want to redirect all content to:

www.example.com/public/...

but prevent direct access to

www.example.com/public/file1/
www.example.com/public/file2/
etc

The final URL should be:

www.example.com/file1/

I've tried this for redirecting and it works - but I dont know how to prevent direct access:

ReWriteCond %{REQUEST_URI} !/public/
RewriteRule ^(.*) public/$1 [L]

回答1:


After spending an inordinate amount of time trying to solve this problem, I found that the solution lies with the under-documented REDIRECT_STATUS environment variable.

Add this to the beginning of your top-level /.htaccess code, and also to any .htaccess files you have under it (e.g. /public/.htaccess):

<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{ENV:REDIRECT_STATUS} !=200
RewriteRule ^ /public%{REQUEST_URI} [L]

</IfModule>

Now, if the user requests example.com/file1 then they are served the file at /public/file1. However, if they request example.com/public/file1 directly then the server will attempt to serve the file at /public/public/file1, which will fail (unless you happen to have a file at that location).

IMPORTANT:

You need to add those lines to all .htaccess files, not just the top-level one in the web root, because if you have any .htaccess files below the web root (e.g. /public/.htaccess) then these will override the top-level .htaccess and users will again be able to access files in /public directly.

Note about variables and redirects:

Performing a redirect (or a rewrite) causes the whole process to start again with the new URI, so any variables that you set before the redirect will no longer be set afterwards. This is done deliberately, because usually you do not want the final result to depend on how you got there (i.e. whether it was via a direct request or via a redirect).

However, for those special occasions where you do want to know how you got to a particular URI, you can use REDIRECT_STATUS. Also, any environment variables set before the redirect (e.g. with SetEnvIf) will still be available after the redirect, but with REDIRECT_ prefixed to the name of the variable (so MY_VAR becomes REDIRECT_MY_VAR).




回答2:


Maybe you should clarify what's the expected behaviour when user tries to reach the real URL:

www.example.com/public/file1/

If by prevent you mean forbid, you could add a rule to respond with a 403

<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{REQUEST_URI} !/public/
RewriteRule ^(.*)$ /public/$1 [L]

RewriteCond %{REQUEST_URI} /public/
RewriteRule ^(.*)$ / [R=403,L]
</IfModule>

Update: The solution above doesn't work!

I realized my previous solution always throws the 403 so it's worthless. Actually, this is kinda tricky because the redirection itself really contains /public/ in the URL.

The solution that really worked for me is to append a secret query string to the redirection and check for this value on URL's containing /public/:

<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{REQUEST_URI} !/public/
RewriteRule ^(.*)$ /public/$1?token=SECRET_TOKEN [L]

RewriteCond %{REQUEST_URI} /public/
RewriteCond %{QUERY_STRING} !token=SECRET_TOKEN
RewriteRule ^(.*)$ / [R=403,NC,L]
</IfModule>

This way www.example.com/file1/ will show file1, but www.example.com/public/file1/ will throw a 403 Forbidden error response.

Concerns about security of this SECRET_TOKEN are discussed here: How secure is to append a secret token as query string in a htaccess rewrite rule?


If your URL's are expected to have it's own query string, like www.example.com/file1/?param=value be sure to add the flag QSA.

RewriteRule ^(.*)$ /public/$1?token=SECRET_TOKEN [QSA,L]


来源:https://stackoverflow.com/questions/45326795/mod-rewrite-redirect-but-prevent-direct-access

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!