I don't understand the difference between break and last (flags of rewrite). The documentation is rather abstruse. I've tried to switch between the two in some of my configs, but I couldn't spot any difference in behavior. Can someone please explain these flags in more detail? Preferably with an example that shows different behavior when flipping one flag to another.
- I don't know the answer, but please update the wiki.nginx.org when you get your answer. Also, the English-language nginx mailing list is quite active, and Igor (the main developer) answers hundreds of questions per month, so perhaps ask there.rmalayter– rmalayter2010-04-12 02:21:06 +00:00Commented Apr 12, 2010 at 2:21
- @rmalayter - this question was asked on the nginx mailing list. Igor answered it but the answer didn't make much sense to me either: pubbs.net/nginx/200908/46047user39883– user398832010-04-12 22:35:18 +00:00Commented Apr 12, 2010 at 22:35
- pubbs.net link is broken as the domain was taken over. Sorry, was not able to find where it should point. ;(Tino– Tino2012-08-09 13:01:53 +00:00Commented Aug 9, 2012 at 13:01
4 Answers
You may have different sets of rewrite rules for different locations. When rewrite module meets last
, it stops processing the current set and the rewritten request is passed once again to find the appropriate location (and the new set of rewriting rules). If the rule ends with break
, the rewriting also stops, but the rewritten request is not passed to another location.
That is, if there are two locations: loc1 and loc2, and there's a rewriting rule in loc1 that changes loc1 to loc2 AND ends with last
, the request will be rewritten and passed to location loc2. If the rule ends with break
, it will belong to location loc1.
- You mean if the rewrite has the break flag it won't search for a matching location block, thus making it belong to location loc1.Martin Fjordvald– Martin Fjordvald2010-04-15 10:19:30 +00:00Commented Apr 15, 2010 at 10:19
-
OP preferred an example. Also, what @minaev wrote, was only a part of the story! So, here we go...
Example 1: No (break or last) flags
server { server_name example.com; root 'path/to/somewhere'; location / { echo 'finally matched location /'; } location /notes { echo 'finally matched location /notes'; } location /documents { echo 'finally matched location /documents'; } rewrite ^/([^/]+.txt)$ /notes/$1; rewrite ^/notes/([^/]+.txt)$ /documents/$1; }
Result:
# curl example.com/test.txt finally matched location /documents
Explanation:
For rewrite
, the flags are optional!
Example 2: Outside location block (break or last)
server { server_name example.com; root 'path/to/somewhere'; location / { echo 'finally matched location /'; } location /notes { echo 'finally matched location /notes'; } location /documents { echo 'finally matched location /documents'; } rewrite ^/([^/]+.txt)$ /notes/$1 break; # or last rewrite ^/notes/([^/]+.txt)$ /documents/$1; # this is not parsed }
Result:
# curl example.com/test.txt finally matched location /notes
Explanation:
Outside the location block, both break
and last
behave in the exact manner...
- no more parsing of rewrite conditions
- Nginx internal engine goes to the next phase (searching for
location
match)
Example 3: Inside location block - "break"
server { server_name example.com; root 'path/to/somewhere'; location / { echo 'finally matched location /'; rewrite ^/([^/]+.txt)$ /notes/$1 break; rewrite ^/notes/([^/]+.txt)$ /documents/$1; # this is not parsed } location /notes { echo 'finally matched location /notes'; } location /documents { echo 'finally matched location /documents'; } }
Result:
# curl example.com/test.txt finally matched location /
Explanation:
Inside a location block, break
flag would do the following...
- no more parsing of rewrite conditions
- Nginx internal engine continues to parse the current
location
block
Example 4: Inside location block - "last"
server { server_name example.com; root 'path/to/somewhere'; location / { echo 'finally matched location /'; rewrite ^/([^/]+.txt)$ /notes/$1 last; rewrite ^/notes/([^/]+.txt)$ /documents/$1; # this is not parsed } location /notes { echo 'finally matched location /notes'; rewrite ^/notes/([^/]+.txt)$ /documents/$1; } location /documents { echo 'finally matched location /documents'; } }
Result:
# curl example.com/test.txt finally matched location /documents
Explanation:
Inside a location block, last
flag would do the following...
- no more parsing of current location context of rewrite conditions
- Nginx internal engine starts to look for another location match based on the result of the
rewrite
result & applies rewrite rule in that location context
Summary:
- When a
rewrite
condition with the flagbreak
orlast
matches, Nginx stops parsing any morerewrites
! - Outside a location block, with
break
orlast
, Nginx does the same job (stops processing anymore rewrite conditions). - Inside a location block, with
break
, Nginx only stops processing anymore rewrite conditions - Inside a location block, with
last
, Nginx stops processing anymore rewrite conditions and then starts to look for a new matching oflocation
block!
Final Note:
I missed to include some more edge cases (actually common problem with rewrites, such as 500 internal error
). But, that'd be out of scope of this question. Probably, example 1 is out of scope, too!
- 3In example 1 would it make a difference if the rewrite rules were placed above the all three location directives?Craig Hicks– Craig Hicks2018-02-09 04:55:46 +00:00Commented Feb 9, 2018 at 4:55
- 4@CraigHicks No, it wouldn't. A rewrite rule has higher precedence and is executed at first before locations are matched.Pothi Kalimuthu– Pothi Kalimuthu2018-02-12 16:16:05 +00:00Commented Feb 12, 2018 at 16:16
- 4This should be the best answer. It's easy to understand by referring to these examples, and reading the nginx documentation.Don Dilanga– Don Dilanga2019-08-15 16:08:49 +00:00Commented Aug 15, 2019 at 16:08
- 1What about "permanent" ? Is it "permanent break" or "break permanent" or both or neither?Jay Brunet– Jay Brunet2020-02-20 21:20:43 +00:00Commented Feb 20, 2020 at 21:20
- 1@PJBrunet As per the official docs that can be seen at nginx.org/r/rewrite, // An optional flag parameter can be one of last, break, redirect and permanent //. Basically, we can not use more than one flag for rewrite statement.Pothi Kalimuthu– Pothi Kalimuthu2020-02-21 07:40:55 +00:00Commented Feb 21, 2020 at 7:40
Summary minaev: within a location block, when you use "last" the rewrites are stopped and a new subrequest is generated which will take all all locations into account. When you use "break" the rewrites are stopped and processing continued within the location you are in.
Adding Maxim (core team nginx developer): statement above into account, outside a location block, "break" behaves just like "last" does since there are no location directives to run here.
Simple and effective way to prevent such sort of problems is to always define "location /". https://mailman.nginx.org/pipermail/nginx/2011-October/029931.html
Only some complements to the previous answers.
- With the
last
flag, the current location block is abandoned, because we switch to a new block that matches the rewritten URI. I felt this was not said clearly enough. With thebreak
flag, we remain in the current block, only the rewrite directives are abandoned. - The directives that stop being executed in the current block are all those of the rewrite module. It is useful to know that these include the
return
directive, but not thetry_files
directive. Atry_files
can be handy after arewrite
. - Even if the rewrite keeps the URI within the current block, there is a distinction between the
break
and thelast
flags. In a way, in both cases, we remain in the same block. Yet, with thelast
flag it is a new entry and all the rewrite directives of the block will be considered again with the rewritten URI. - The detour outside the block and then back in the block could make a difference even if the URI is rewritten in the same way, because some environment variables could be affected, though, I must admit that I tried to find a case where this happens and could not find any.