48

The site is on shared hosting. I need to password protect a single URL.

http://www.example.com/pretty/url

Obviously that's not a physical file path I'm trying to protect, it's just that particular URL.

Any quick solution with .htaccess?

Jan Hančič
  • 53,269
  • 16
  • 95
  • 99
BadHorsie
  • 14,135
  • 30
  • 117
  • 191

10 Answers10

86

You should be able to do this using the combination of mod_env and the Satisfy any directive. You can use SetEnvIf to check against the Request_URI, even if it's not a physical path. You can then check if the variable is set in an Allow statement. So either you need to log in with password, or the Allow lets you in without password:

# Do the regex check against the URI here, if match, set the "require_auth" var
SetEnvIf Request_URI ^/pretty/url require_auth=true

# Auth stuff
AuthUserFile /var/www/htpasswd
AuthName "Password Protected"
AuthType Basic

# Setup a deny/allow
Order Deny,Allow
# Deny from everyone
Deny from all
# except if either of these are satisfied
Satisfy any
# 1. a valid authenticated user
Require valid-user
# or 2. the "require_auth" var is NOT set
Allow from env=!require_auth
Jon Lin
  • 142,182
  • 29
  • 220
  • 220
  • Works a treat, thanks. Wasn't able to find anything like this elsewhere. – BadHorsie Jan 30 '13 at 15:28
  • 1
    Hi this snippet works in apache 2.2 but not in apache 2.4 any ideas on how I can migrate it to 2.4? My problem is password protect is being asked in all URLs – Ram G Athreya Oct 14 '14 at 12:07
  • 5
    same here, password asked everywhere – useless Mar 28 '15 at 17:48
  • 1
    same here its working on " Server:Apache/2.2.15 (CentOS)" and not working on "Server:Apache/2.2.3 (CentOS)" – nilesh Jul 20 '15 at 13:11
  • can i set a username and password to be used? Is it possible to just require a password without a username? – edank Oct 28 '15 at 23:15
  • 2
    @edank No, you must have a username as well. If you want to use a specific username, you must change `Require valid-user` to `Require user [your username]`. You cannot define the password in the .htaccess file as it would be insecure. You must use [htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) to create a .htpasswd file on your server, which contains the users and their passwords. – BadHorsie Mar 10 '16 at 11:51
  • This doesn't work if I want to password protect a single URL, but for all other URLs I want to follow the rules in the vhost conf. In this example all URLs except one will be allowed. What if I have an IP restriction in the vhost conf? – windyjonas Apr 13 '16 at 09:41
  • I tried this solution but needed to add `ErrorDocument 401 "Authorisation Required"` below `AuthType Basic` for it to work. I am on **Apache/2.4.23** – Splendiferous Dec 07 '16 at 14:29
22

You can use <LocationMatch> or simply <Location> inside your <VirtualHost> directive to do this (assuming you have access to your httpd.conf / vhost.conf - alternatively you could put something similar in a .htaccess in your document root if you have to configure your site that way).

For example:

<VirtualHost *:80>
  ServerName www.example.com
  DocumentRoot /var/www/blabla
  # Other usual vhost configuration here
  <Location /pretty/url>
    AuthUserFile /path/to/.htpasswd
    AuthGroupFile /dev/null
    AuthName "Password Protected"
    AuthType Basic
    require valid-user
  </Location>
</VirtualHost>

You might find <LocationMatch> more useful if you want to match a regular expression against your pretty URL. The documentation is here.

Coder
  • 2,833
  • 2
  • 22
  • 24
14

Since Rick stated in a comment that no answer in this question works, here is the snippet I use:

AuthName "Protected Area"
AuthType Basic
AuthUserFile /path/to/your/.htpasswd
AuthGroupFile /dev/null

SetEnvIf Request_URI .* noauth
SetEnvIf Request_URI the_uri_you_want_to_protect !noauth
SetEnvIf Request_URI another_uri !noauth
SetEnvIf Request_URI add_as_many_as_you_want !noauth

<RequireAny>
  Require env noauth
  Require valid-user
</RequireAny>

If you need support for Apache 2.2 AND Apache 2.4 (apparently there are setups where both versions run in parallel...):

AuthName "Protected Area"
AuthType Basic
AuthUserFile /path/to/your/.htpasswd
AuthGroupFile /dev/null

SetEnvIf Request_URI .* noauth
SetEnvIf Request_URI the_uri_you_want_to_protect !noauth
SetEnvIf Request_URI another_uri !noauth
SetEnvIf Request_URI add_as_many_as_you_want !noauth

<IfModule mod_authz_core.c>
    <RequireAny>
        Require env noauth
        Require valid-user
    </RequireAny>
</IfModule>

<IfModule !mod_authz_core.c>
    Order Deny,Allow
    Deny from all
    Satisfy any
    Require valid-user
    Allow from env=noauth
</IfModule>

The code for Apache 2.2 is taken from Jon Lin.

Imanuel
  • 3,596
  • 4
  • 24
  • 46
  • 1
    This is it! You rock the world @Pharaoh (tested on Apache/2.4.27 (cPanel)) – Rick Jul 21 '17 at 15:32
  • This is the best solution to the simple problem. I had a different issue where I was protecting some pdf files using environment variable, but since it was overlapping with the other Environment variable, it was pain! Thank you for your answer. – Abhishek Mugal Feb 20 '18 at 08:39
  • This is the only snippet that worked for me. I thought we could use block to have different behaviour per URL, but I think it doesn't work if it's in .htaccess sadly (not sure, untested). – davidwebca Nov 30 '21 at 21:10
  • this does not work for apache 2.4.52 – Dariyoush Feb 10 '23 at 17:31
  • "does not work" does not tell me much ;) Is there an error message? – Imanuel Feb 10 '23 at 21:53
  • Sorry, let me try again :) I use this as .htaccess in the subdomain directory like ´meet.example.com´ and also in apache configuration. the result is I can access the URL still. I would like to set a password on ´meet.example.com´ but still can access ´meet.example.com/other_pages´. any idea what am I doing wrong? – Dariyoush Feb 12 '23 at 09:05
  • Well, the possibilities are endless :) Is the .htaccess being parsed? Is your URI correct? Try to remove "require env noauth" to see if the behaviour changes. if it does, your URI might not be correct. If not, htaccess is not being parsed. Then take it from there ;) – Imanuel Feb 12 '23 at 13:05
  • @Imanuel thank you. you were right, .htaccess was not being parsed. I made sure that it is. but it still does not work (the URL is correct the format is meet.example.com, without https). when I remove "require env noauth" it says unauthorized! which means it is reading the file. any idea? – Dariyoush Feb 12 '23 at 13:54
  • Check what Apache matches for REQUEST_URI: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond Spoiler: Not the domain, only the path – Imanuel Feb 14 '23 at 07:10
6

All the provided solutions didn't work for me. I figured following directives do the trick:

SetEnvIf Request_URI ^/page-url auth=1

AuthName "Please login"
AuthType Basic
AuthUserFile "/www/live.example.com/files/html/.htpasswd"

# first, allow everybody
Order Allow,Deny
Satisfy any
Allow from all
Require valid-user
# then, deny only if required
Deny from env=auth
Stefan
  • 8,456
  • 3
  • 29
  • 38
3

In Apache2.4 you can do:

<If "%{REQUEST_URI} =~ m#/pretty/url/?#i">
    AuthUserFile /var/www/htpasswd
    AuthName "Password protected"
    AuthType Basic
    Require valid-user
</If>
Thomas Leu
  • 826
  • 4
  • 13
2

I updated Jon Lin's code for the purpose of protecting all URLs except for one - for instance robots.txt at the vhost's root. These are Apache 2.2 compliant directives.

<ifmodule mod_setenvif.c>
SetEnv  require_no_auth=false
SetEnvIf Request_URI "^/robots.txt" require_no_auth=true

AuthType Basic
AuthName "Restricted"
AuthUserFile /home/someuser/.htpasswd

# Setup a deny/allow
Order Deny,Allow
# Deny from everyone
Deny from all
# 1. a valid authenticated user
Require valid-user
# or 2. the "require_no_auth" var is set
Allow from env=require_no_auth
# except if either of these are satisfied
Satisfy any
</ifmodule>

Same code for Apache 2.4

<ifmodule mod_setenvif.c>
SetEnv  require_no_auth=false
SetEnvIf Request_URI "^/robots.txt" require_no_auth=true

AuthType Basic
AuthBasicProvider file
AuthName "Restricted"
AuthUserFile /home/someuser/.htpasswd

# grant access if either of these are satisfied
# 1. a valid authenticated user
Require valid-user
# or 2. the "require_no_auth" var is set
Require env require_no_auth
</ifmodule>
Pierre Priot
  • 101
  • 1
  • 4
0

What about redirecting the user to a password protected subfolder?

.htaccess

RewriteCond %{HTTP_COOKIE} !BadHorsie=secret_cookie_key
RewriteRule ^(pretty/url)$ /protected/login.php?url=$1 [R=307,L]

protected/.htaccess

AuthUserFile /usr/www/{YOUR_PATH}/protected/.htpasswd
AuthGroupFile /dev/null
AuthName "Protected"
AuthType Basic
require user BadHorsie

protected/.htpasswd

BadHorsie:$apr1$fFbaaVdF$Q5ql58g7R4qlpMUDb/5A0/

protected/login.php

<?php
if (isset($_GET['url']) && $_GET['url'] && $_GET['url'][0] != '/' && strpos($_GET['url'], '//') === false) {
    setcookie('BadHorsie', 'secret_cookie_key', 0, '/');
    header('Location: /' . $_GET['url'], true, 307);
    exit;
}
?>

What happens

  1. User requests example.com/pretty/url
  2. 307 redirect to example.com/protected/login.php?url=pretty/url
  3. login
  4. on success: user obtains session cookie with secret key
  5. 307 redirect back to example.com/pretty/url
  6. User obtains secret content

Note: Of course the "session cookie and back-redirecting"-mechanism is fully optional. Finally you could serve your secret content directly through protected/login.php. I showed this way only for inspiration.

Optional: Do not use PHP and set the cookie through .htaccess.

mgutt
  • 5,867
  • 2
  • 50
  • 77
0
  1. First of all, you need to create a new user/password:

This command:

htpasswd -c /etc/apache2/.htpasswd myuser1

If you are using Bitnami Apps (Open edx, for example), you need follow this instruction:

htpasswd -c /opt/bitnami/apps/edx/conf/.htpasswd myuser1
  1. Then if you have defined a virtual host, you need to add the following lines to your apache config file.

This config:

<VirtualHost *:80>
...

<Location />
    Deny from all
    #Allow from (Set IP to allow access without password)
    AuthUserFile /etc/apache2/.htpasswd
    AuthName "Restricted Area"
    AuthType Basic
    Satisfy Any
    require valid-user
</Location>
...

</VirtualHost>
  1. In the case of Bitnami Apps, you need to add these configs inside ‘/opt/bitnami/apps/edx/conf/httpd-lms.conf’:

This config to protect all URLs.:

<Location />
    Deny from all
    #Allow from (Set IP to allow access without password)
    AuthUserFile /opt/bitnami/apps/edx/conf/.htpasswd
    AuthName "Restricted Area"
    AuthType Basic
    Satisfy Any
    require valid-user
</Location>
  1. Finally, you need to restart the Apache server.

In Bitnami Apps:

/opt/bitnami/ctlscript.sh restart apache
Mohammad
  • 337
  • 3
  • 6
-1

the solution above is too busy and messed up a bit.. I go for simplicity and clarity like this

<Files "protected.html">
 AuthName "Username and password required"
 AuthUserFile /home/fullpath/.htpasswd
 Require valid-user
 AuthType Basic
</Files>

The above is to be put into your htaccess file where the "protected.html" is the file you want to protect (it can be anything you wish, for example myphoto.jpg or a document.zip, just re-name it to your preference). The AuthUserFile is the full path to your password file. You are done.

Although you may have to make sure the prerequisites for this are set. AllowOverride and AuthConfig are required in Apache directory tag configs for the code to work but normally it is pre-set out of the box so you should be safe and fine unless you are making your own custom build.

Dennis
  • 19
  • 1
  • 1
    the question clearly states it's not a physical URL they are trying to protect. so protecting one single file can't help here. – shlomia Jan 27 '16 at 08:55
-1

Unfortunately I can't comment on @jon-lin answer. So, I create a new answer. My adapted solution in an apache 2.4 environment is:

#
# password protect /pretty/url URIs
#

AuthType Basic
AuthName 'Authentication required'
AuthUserFile /path/to/.htpasswd

# Restrict access to some urls
SetEnvIf Request_URI ^/pretty/url  auth=1

<RequireAll>
  # require the auth variable to be set
  Require env auth
  # require an valid-user
  Require valid-user
</RequireAll>
simne7
  • 194
  • 1
  • 1
  • 12
  • 1
    With this, you can't open any other url than your `/pretty/url` because `Require env auth` will forbid access if the variable is not set – Imanuel Jun 09 '16 at 06:49
  • That seems the case @Pharaoh . So how do we fix this? Nothing on this page seems to work properly... (I am on Apache/2.4.27 (cPanel) btw.) – Rick Jul 21 '17 at 15:05