|
1 | | -.htaccess for nginx |
2 | | -=================== |
| 1 | +# .htaccess for nginx |
3 | 2 |
|
4 | | -**.htaccess for nginx** enables [nginx](https://nginx.org/en/) high performance webserver to deal with `.htaccess` files. |
| 3 | +**.htaccess for nginx** enables the [nginx](https://nginx.org/en/) high performance webserver to deal with `.htaccess` files. |
5 | 4 |
|
6 | | -`.htaccess` files are mainly used for access control and URL rewrite instructions and are widely known across the web community. Originally designed for [Apache](https://www.apache.org/), there is no native implementation available for nginx. |
| 5 | +`.htaccess` files are mainly used for access control and URL rewrite instructions and are widely known across the web community. Originally designed for [Apache](https://www.apache.org/), there is no native implementation available for nginx. While there is a [legitimate reason for this](https://www.nginx.com/resources/wiki/start/topics/examples/likeapache-htaccess/), there would be huge practical benefit if nginx was able to support this. |
7 | 6 |
|
8 | 7 | **.htaccess for nginx** is efficient and elegant, using micro caching and various performance tweaks right out of the box. It is effortless in its installation and usage. The plugin's deeply integrated approach is ideal for webhosters, who are looking for mixed technology solutions using only nginx and nothing else. |
9 | 8 |
|
10 | | -Stop Using Apache |
11 | | ------------------ |
| 9 | +## Stop using Apache |
12 | 10 |
|
13 | 11 | * Apache is slow. |
14 | 12 | * Apache is wasting resources. |
15 | 13 | * Compared to nginx, Apache is poorly and inconsistently designed. |
16 | 14 | * Apache's monolithic design prevents it from scaling properly, while nginx is capable of handling tens of thousands of simultaneous connections with ease. |
17 | 15 | * Switching to nginx heavily improves performance, efficiency and security. |
18 | 16 |
|
19 | | -* * * |
20 | | - |
21 | | -Reasons for .htaccess in nginx |
22 | | ------------------------------- |
| 17 | +## Reasons for .htaccess in nginx |
23 | 18 |
|
24 | 19 | When using nginx, there are many **legitimate reasons** to support `.htaccess` files. |
25 | 20 |
|
26 | 21 | * **Mixed technology.** Imagine using NodeJS and PHP side by side, running on one stable nginx webserver. When dealing with customer webspace, using Apache and nginx together (one proxying the other) is possible, however this adds unnecessary layers of redundancy and heavily wastes valuable server resources. |
27 | | -* **Ease of use.** Everybody knows how to use `.htaccess` files. [January 2020, more than 24% of all active websites out there are still run by Apache's webserver](https://web.archive.org/web/20200130141042/https://news.netcraft.com/archives/2020/01/21/january-2020-web-server-survey.html), capable of utilizing `.htaccess` files. If nginx had a way to support this feature, this number would be going down significantly, making the web faster. |
| 22 | +* **Ease of use.** Everybody knows how to use `.htaccess` files. As pf January 2020, [more than 24% of all active websites](https://web.archive.org/web/20200130141042/https://news.netcraft.com/archives/2020/01/21/january-2020-web-server-survey.html) are still run on Apache and thus capable of utilizing `.htaccess` files. If nginx had a way to support this feature, this number would be going down significantly, making the web faster. |
28 | 23 | * **Legacy.** Just use your old code, without worrying if someone could access a protected directory inside any library you just forgot to handle in your nginx config. |
29 | | -* **Plug'n'play.** No need to convert `.htaccess` files for nginx and fix all the errors, rant about unsupported or oddly mixed up auto-generated config goo coming from a random online converter. |
| 24 | +* **Plug and play.** No need to convert `.htaccess` files for nginx and fix all the errors, rant about unsupported or oddly mixed up auto-generated config goo coming from a random online converter. |
30 | 25 | * **Justified.** Apache performs multiple file reads anyway, so .htaccess for nginx cannot make it worse than Apache, right? In fact, with our built-in micro caching mechanism both, CPU and I/O load are reduced drastically compared to Apache's implementation. |
31 | 26 | * **For webhosters.** Today, webhosters still need to provide an interface for their customers to change certain aspects of their webserver's behaviour. The decades long and proven `.htaccess` file does just that. |
32 | 27 |
|
33 | | -* * * |
34 | 28 |
|
35 | | -Performance |
36 | | ------------ |
| 29 | +## Performance |
37 | 30 |
|
38 | | -**.htaccess for nginx is incredibly lightweight and fast!** It is writting from ground up with performance optimizations in mind. Even with low-end hardware it **adds less than 1 millisecond to your response time**, also for quite complex rewrite structures with server variables. |
| 31 | +**.htaccess for nginx is incredibly lightweight and fast!** It is written from the ground up with performance optimizations in mind. Even with low-end hardware it adds less than 1 millisecond to your response time, despite supporting quite complex rewrite structures with server variables. |
39 | 32 |
|
40 | | -Physical **memory usage** of this plugin is insanely low, just **less than 10 KB** for each nginx worker process, and it doesn't increase with more requests. |
| 33 | +Physical memory usage of this plugin is insanely low, under 10 KB for each nginx worker process, and it doesn't increase with more requests. |
41 | 34 |
|
42 | | -* * * |
43 | 35 |
|
44 | | -Requirements |
45 | | ------------- |
| 36 | +## Requirements |
46 | 37 |
|
47 | | -* Unix environment |
48 | | -* `nginx` with Lua module |
| 38 | +* Debian or Fedora environment |
| 39 | +* `nginx` v1.19+ with Lua module |
49 | 40 | * `curl` command-line tool |
50 | | - |
51 | | -### Optional Dependencies |
52 | | - |
53 | | -* `htpasswd` utility (`apache2-utils` package) for .htpasswd hashing functions (required for Basic HTTP Authentication) |
54 | | -* `getent` utility (`libc-bin` package) for hostname lookups (e.g. `Deny from _domainname.tld_`) |
55 | | - |
56 | | -* * * |
57 | | - |
58 | | -Installation |
59 | | ------------- |
60 | | - |
61 | | -* Install nginx **with Lua module**: `apt-get install nginx` |
62 | | -* Install [Lua](https://www.lua.org/download.html): `apt-get install lua5.2` |
63 | | -* Save this plugin directory into `/etc/nginx/`, so that you can access `/etc/nginx/htaccess-for-nginx/htaccess.lua` |
64 | | -* Add the following configuration to your `http {}` context: `lua_shared_dict htaccess 16m;` |
65 | | -* Configure your hosts (within the `server {}` context): `rewrite_by_lua_file /etc/nginx/htaccess-for-nginx/htaccess.lua;` |
66 | | - |
67 | | -### Hints and Common Practice |
68 | | - |
69 | | -* Depending on your operating system, the installation process may vary. |
70 | | -* Make sure to have **Lua version 5.2** installed on your system. |
71 | | -* You can clone this repository to any directory of your choice. Just make sure to adjust the paths accordingly. |
72 | | -* If you don't set the `lua_shared_dict` setting in `http {}`, this plugin will refuse to work. It represents a caching system, used on a short-term per-request basis. Values (`.htaccess` lines) are usually cached less than 100 milliseconds, but kept in memory as long as there are active connections. You can choose to assign any other memory amount to it, although 16 MB should be more than enough. |
73 | | -* ⚠️ Note that global configuration within your `http {}` context is technically possible. However, if you want to keep the good nginx performance for your new, non-legacy projects, you are **highly encouraged** to use this plugin in `server {}` context only. |
74 | | -* To make your life easier, just create a config snipped and write e.g. `include snippets/htaccess.conf` in the `server {}` config. |
75 | | - |
76 | | -* * * |
77 | | - |
78 | | -Usage — Testing it |
79 | | ------------------- |
| 41 | +* Optional: `htpasswd` utility (`apache2-utils` package) for `.htpasswd` hashing functions (required for Basic HTTP Authentication) |
| 42 | +* Optional: `getent` utility (`libc-bin` package) for hostname lookups (e.g. `Deny from _domainname.tld_`) |
| 43 | + |
| 44 | + |
| 45 | +## Installation |
| 46 | + |
| 47 | +1. Install nginx (with the [Lua module](https://github.com/openresty/lua-nginx-module) if you don't already have it) and luajit. |
| 48 | + 1. Debian: `apt-get install nginx luajit` |
| 49 | + 1. Fedora: `yum install nginx luajit` |
| 50 | +1. uild and install the plugin into an appropriate directory accessible by the nginx process, e.g., |
| 51 | + ```bash |
| 52 | + luajit -b htaccess.lua /etc/nginx/lua/htaccess.lbc |
| 53 | + ``` |
| 54 | +1. Add the following configuration to the nginx `http {}` context: |
| 55 | + ```nginx |
| 56 | + http { |
| 57 | + ... |
| 58 | + lua_shared_dict htaccess 16m; |
| 59 | + ... |
| 60 | + } |
| 61 | + ``` |
| 62 | + This represents a caching system, used on a short-term per-request basis. `.htaccess` lines are usually cached as values for less than 100 milliseconds, but kept in memory as long as there are active connections. You can choose to assign any other memory amount to it, although 16 MB should be more than enough. |
| 63 | +1. Configure the nginx `server {}` context(s) to use the plugin: |
| 64 | + ```nginx |
| 65 | + server { |
| 66 | + ... |
| 67 | + rewrite_by_lua_file /path/to/htaccess.lua; |
| 68 | + # or reference the bytecode instead |
| 69 | + # rewrite_by_lua_file /path/to/htaccess.lbc; |
| 70 | + ... |
| 71 | + } |
| 72 | + ``` |
| 73 | + |
| 74 | +## Example |
80 | 75 |
|
81 | 76 | Create an `.htaccess` file in a directory of your host with the following content: |
82 | 77 |
|
83 | | -`Order deny,allow |
84 | | -Deny from all` |
| 78 | +```apache |
| 79 | +Order deny,allow |
| 80 | +Deny from all |
| 81 | +``` |
85 | 82 |
|
86 | 83 | When trying to access a file inside this directory through your browser, access should be denied by receiving an `HTTP 403` response. |
87 | 84 |
|
88 | | -* * * |
89 | | - |
90 | | -Supported Features |
91 | | ------------------- |
92 | | - |
93 | | -**We compiled a [complete list of implemented `.htaccess` directives and variables](https://htaccess-for-nginx.com/features).** |
94 | 85 |
|
95 | | -This plugin tries to make things as secure as possible. **Wherever an unclear situation occurs, access will be denied** to prevent unintended access, e.g. if unsupported, security-critical directives are being used (HTTP 500 response). Unsupported, non-security-related directives will be ignored. |
| 86 | +## Supported Syntax |
| 87 | + |
| 88 | +The following tables came from [this page](https://htaccess-for-nginx.com/features). |
| 89 | + |
| 90 | +### Sections |
| 91 | + |
| 92 | +| Module | Section | Supported | Notes | |
| 93 | +| ------ | ------- | --------- | ----- | |
| 94 | +core | `<Else>` | No | |
| 95 | +core | `<ElseIf>` | No | |
| 96 | +core | `<Files>` | Yes | |
| 97 | +core | `<FilesMatch>` | Yes | |
| 98 | +core | `<If>` | No | |
| 99 | +core | `<IfDefine>` | Never | Impossible to be implemented. Apache specific |
| 100 | +core | `<IfDirective>` | Yes | |
| 101 | +core | `<IfFile>` | Yes | |
| 102 | +core | `<IfModule>` | Yes | Emulating supported modules according to supported directives |
| 103 | +core | `<IfSection>` | Yes | |
| 104 | +core | `<Limit>` | Yes | |
| 105 | +core | `<LimitExcept>` | Yes | |
| 106 | +mod_authz_core | `<RequireAll>` | No | |
| 107 | +mod_authz_core | `<RequireAny>` | No | |
| 108 | +mod_authz_core | `<RequireNone>` | No | |
| 109 | +mod_version | `<IfVersion>` | Yes | The version will be simulated as Apache 2.4.0 |
| 110 | + |
| 111 | +### Directives |
| 112 | + |
| 113 | +Directives not listed below are not supported. |
| 114 | + |
| 115 | +| Module | Directive | Supported | Notes | |
| 116 | +| ------ | --------- | --------- | ----- | |
| 117 | +core | `AcceptPathInfo` | No | |
| 118 | +core | `AddDefaultCharset` | No | |
| 119 | +core | `CGIMapExtension` | No | |
| 120 | +core | `CGIPassAuth` | No | |
| 121 | +core | `CGIVar` | No | |
| 122 | +core | `ContentDigest` | No | |
| 123 | +core | `DefaultType` | No | |
| 124 | +core | `EnableMMAP` | No | |
| 125 | +core | `EnableSendfile` | No | |
| 126 | +core | `ErrorDocument` | No | |
| 127 | +core | `FileETag` | No | |
| 128 | +core | `ForceType` | No | |
| 129 | +core | `LimitRequestBody` | No | |
| 130 | +core | `LimitXMLRequestBody` | No | |
| 131 | +core | `Options` | No | |
| 132 | +core | `QualifyRedirectURL` | No | |
| 133 | +core | `RLimitCPU` | Never | Rarely used and not practical for nginx |
| 134 | +core | `RLimitMEM` | Never | Rarely used and not practical for nginx |
| 135 | +core | `RLimitNPROC` | Never | Rarely used and not practical for nginx |
| 136 | +core | `ScriptInterpreterSource` | No | |
| 137 | +core | `ServerSignature` | No | |
| 138 | +core | `SetHandler` | No | |
| 139 | +core | `SetInputFilter` | No | |
| 140 | +core | `SetOutputFilter` | No | |
| 141 | +mod_access_compat | `Allow` | Yes | `Allow from domainname.tld` requires `getent` command line tool |
| 142 | +mod_access_compat | `Deny` | Yes | `Deny from domainname.tld` requires `getent` command line tool |
| 143 | +mod_access_compat | `Order` | Yes | |
| 144 | +mod_access_compat | `Satisfy` | Never | Security reasons. `Satisfy All` assumed |
| 145 | +mod_actions | `Action` | Never | Security reasons. CGI request handling must be in main host config |
| 146 | +mod_alias | `Redirect` | Yes | |
| 147 | +mod_alias | `RedirectMatch` | Yes | |
| 148 | +mod_alias | `RedirectPermanent` | Yes | |
| 149 | +mod_alias | `RedirectTemp` | Yes | |
| 150 | +mod_auth_basic | `AuthBasicAuthoritative` | No | |
| 151 | +mod_auth_basic | `AuthBasicFake` | No | |
| 152 | +mod_auth_basic | `AuthBasicProvider` | No | |
| 153 | +mod_auth_basic | `AuthBasicUseDigestAlgorithm` | No | |
| 154 | +mod_auth_digest | `*` | No | |
| 155 | +mod_auth_form | `*` | No | |
| 156 | +mod_authn_anon | `*` | No | |
| 157 | +mod_authn_core | `AuthName` | Yes | |
| 158 | +mod_authn_core | `AuthType` | Partially | Only `AuthType Basic` supported |
| 159 | +mod_authn_dbm | `*` | No | |
| 160 | +mod_authn_file | `AuthUserFile` | Yes | |
| 161 | +mod_authn_socache | `*` | No | |
| 162 | +mod_authnz_ldap | `*` | No | |
| 163 | +mod_authz_core | `AuthMerging` | No | |
| 164 | +mod_authz_core | `Require` | Partially | Require group, host, expr not supported |
| 165 | +mod_authz_dbm | `*` | No | |
| 166 | +mod_authz_groupfile | `*` | No | |
| 167 | +mod_autoindex | `AddAlt` | No | |
| 168 | +mod_autoindex | `AddAltByEncoding` | No | |
| 169 | +mod_autoindex | `AddAltByType` | No | |
| 170 | +mod_autoindex | `AddDescription` | No | |
| 171 | +mod_autoindex | `AddIcon` | No | |
| 172 | +mod_autoindex | `AddIconByEncoding` | No | |
| 173 | +mod_autoindex | `AddIconByType` | No | |
| 174 | +mod_autoindex | `DefaultIcon` | No | |
| 175 | +mod_autoindex | `HeaderName` | No | |
| 176 | +mod_autoindex | `IndexHeadInsert` | No | |
| 177 | +mod_autoindex | `IndexIgnore` | No | |
| 178 | +mod_autoindex | `IndexIgnoreReset` | No | |
| 179 | +mod_autoindex | `IndexOptions` | No | |
| 180 | +mod_autoindex | `IndexOrderDefault` | No | |
| 181 | +mod_autoindex | `IndexStyleSheet` | No | |
| 182 | +mod_autoindex | `ReadmeName` | No | |
| 183 | +mod_cern_meta | `*` | No | Rarely used |
| 184 | +mod_charset_lite | `CharsetDefault` | No | |
| 185 | +mod_charset_lite | `CharsetOptions` | No | |
| 186 | +mod_charset_lite | `CharsetSourceEnc` | No | |
| 187 | +mod_dir | `DirectoryCheckHandler` | No | |
| 188 | +mod_dir | `DirectoryIndex` | No | |
| 189 | +mod_dir | `DirectoryIndexRedirect` | No | |
| 190 | +mod_dir | `DirectorySlash` | No | |
| 191 | +mod_dir | `FallbackResource` | No | |
| 192 | +mod_env | `PassEnv` | No | |
| 193 | +mod_env | `SetEnv` | No | |
| 194 | +mod_env | `UnsetEnv` | No | |
| 195 | +mod_expires | `ExpiresActive` | No | |
| 196 | +mod_expires | `ExpiresByType` | No | |
| 197 | +mod_expires | `ExpiresDefault` | No | |
| 198 | +mod_filter | `AddOutputFilterByType` | No | |
| 199 | +mod_filter | `FilterChain` | No | |
| 200 | +mod_filter | `FilterDeclare` | No | |
| 201 | +mod_filter | `FilterProtocol` | No | |
| 202 | +mod_filter | `FilterProvider` | No | |
| 203 | +mod_headers | `Header` | No | |
| 204 | +mod_headers | `RequestHeader` | No | |
| 205 | +mod_imagemap | `*` | No | |
| 206 | +mod_include | `SSIErrorMsg` | No | |
| 207 | +mod_include | `SSITimeFormat` | No | |
| 208 | +mod_include | `SSIUndefinedEcho` | No | |
| 209 | +mod_include | `XBitHack` | No | |
| 210 | +mod_isapi | `*` | No | |
| 211 | +mod_ldap | `*` | No | |
| 212 | +mod_logio | `*` | No | |
| 213 | +mod_lua | `*` | No | |
| 214 | +mod_mime | `AddCharset` | No | |
| 215 | +mod_mime | `AddEncoding` | No | |
| 216 | +mod_mime | `AddHandler` | No | |
| 217 | +mod_mime | `AddInputFilter` | No | |
| 218 | +mod_mime | `AddLanguage` | No | |
| 219 | +mod_mime | `AddOutputFilter` | No | |
| 220 | +mod_mime | `AddType` | Yes | |
| 221 | +mod_mime | `DefaultLanguage` | No | |
| 222 | +mod_mime | `MultiviewsMatch` | No | |
| 223 | +mod_mime | `RemoveCharset` | No | |
| 224 | +mod_mime | `RemoveEncoding` | No | |
| 225 | +mod_mime | `RemoveHandler` | No | |
| 226 | +mod_mime | `RemoveInputFilter` | No | |
| 227 | +mod_mime | `RemoveLanguage` | No | |
| 228 | +mod_mime | `RemoveOutputFilter` | No | |
| 229 | +mod_mime | `RemoveType` | No | |
| 230 | +mod_negotiation | `ForceLanguagePriority` | No | |
| 231 | +mod_negotiation | `LanguagePriority` | No | |
| 232 | +mod_reflector | `*` | Never | Security reasons |
| 233 | +mod_rewrite | `RewriteBase` | Yes | |
| 234 | +mod_rewrite | `RewriteCond` | Yes | |
| 235 | +mod_rewrite | `RewriteEngine` | Yes | |
| 236 | +mod_rewrite | `RewriteOptions` | No | |
| 237 | +mod_rewrite | `RewriteRule` | Yes | |
| 238 | +mod_session | `*` | No | |
| 239 | +mod_setenvif | `BrowserMatch` | No | |
| 240 | +mod_setenvif | `BrowserMatchNoCase` | No | |
| 241 | +mod_setenvif | `SetEnvIf` | No | |
| 242 | +mod_setenvif | `SetEnvIfExpr` | No | |
| 243 | +mod_setenvif | `SetEnvIfNoCase` | No | |
| 244 | +mod_speling | `CheckCaseOnly` | No | |
| 245 | +mod_speling | `CheckSpelling` | No | |
| 246 | +mod_ssl | `SSLCipherSuite` | No | |
| 247 | +mod_ssl | `SSLOptions` | No | |
| 248 | +mod_ssl | `SSLRenegBufferSize` | No | |
| 249 | +mod_ssl | `SSLRequire` | No | |
| 250 | +mod_ssl | `SSLRequireSSL` | No | |
| 251 | +mod_ssl | `SSLUserName` | No | |
| 252 | +mod_ssl | `SSLVerifyClient` | No | |
| 253 | +mod_ssl | `SSLVerifyDepth` | No | |
| 254 | +mod_substitute | `Substitute` | No | |
| 255 | +mod_substitute | `SubstituteInheritBefore` | No | |
| 256 | +mod_substitute | `SubstituteMaxLineLength` | No | |
| 257 | +mod_usertrack | `CookieDomain` | No | |
| 258 | +mod_usertrack | `CookieExpires` | No | |
| 259 | +mod_usertrack | `CookieHTTPOnly` | No | |
| 260 | +mod_usertrack | `CookieName` | No | |
| 261 | +mod_usertrack | `CookieSameSite` | No | |
| 262 | +mod_usertrack | `CookieSecure` | No | |
| 263 | +mod_usertrack | `CookieStyle` | No | |
| 264 | +mod_usertrack | `CookieTracking` | No | |
| 265 | + |
| 266 | +### Variables |
| 267 | + |
| 268 | +Variables not listed below are not supported. |
| 269 | + |
| 270 | +| Variable | Supported | Notes | |
| 271 | +| ------ | --------- | --------- | ----- | |
| 272 | +`HTTP_*` | Yes | all standard and non-standard HTTP header fields are supported |
| 273 | +`HTTPS` | Yes |
| 274 | +`DOCUMENT_ROOT` | Yes |
| 275 | +`SERVER_ADDR` | Yes |
| 276 | +`SERVER_NAME` | Yes |
| 277 | +`SERVER_PORT` | Yes |
| 278 | +`SERVER_PROTOCOL` | Yes |
| 279 | +`REMOTE_ADDR` | Yes |
| 280 | +`REMOTE_HOST` | Yes |
| 281 | +`REMOTE_USER` | Yes |
| 282 | +`REMOTE_PORT` | Yes |
| 283 | +`REQUEST_METHOD` | Yes |
| 284 | +`REQUEST_FILENAME` | Yes |
| 285 | +`REQUEST_URI` | Yes |
| 286 | +`QUERY_STRING` | Yes |
| 287 | +`SCRIPT_FILENAME` | Yes |
| 288 | +`REQUEST_SCHEME` | Yes |
| 289 | +`THE_REQUEST` | Yes |
| 290 | +`IPV6` | Yes |
| 291 | +`TIME` | Yes |
| 292 | +`TIME_YEAR` | Yes |
| 293 | +`TIME_MON` | Yes |
| 294 | +`TIME_DAY` | Yes |
| 295 | +`TIME_HOUR` | Yes |
| 296 | +`TIME_MIN` | Yes |
| 297 | +`TIME_SEC` | Yes |
| 298 | +`TIME_WDAY` | Yes |
| 299 | + |
| 300 | + |
| 301 | +## Tips |
| 302 | + |
| 303 | +* This plugin tries to make things as secure as possible. **Wherever an unclear situation occurs, access will be denied** to prevent unintended access, e.g. if unsupported, security-critical directives are being used (HTTP 500 response). Unsupported, non-security-related directives will be ignored. |
| 304 | +* Global configuration within your `http {}` context is technically possible. However, you are encouraged to use this plugin only in the `server {}` contexts that will need it. |
| 305 | +* To make your life easier, you can create a config snippet and include it in the `server {}` config: |
| 306 | + ```nginx |
| 307 | + server { |
| 308 | + ... |
| 309 | + include snippets/htaccess.conf |
| 310 | + ... |
| 311 | + } |
| 312 | + ``` |
96 | 313 |
|
97 | 314 | * * * |
98 | 315 |
|
|
0 commit comments