Add a rewrite directive for the .well-known location so we don't hit existing rewrites
diff --git a/certbot-nginx/certbot_nginx/http_01.py b/certbot-nginx/certbot_nginx/http_01.py
index c25081ae04e..2885e7ac0fc 100644
--- a/certbot-nginx/certbot_nginx/http_01.py
+++ b/certbot-nginx/certbot_nginx/http_01.py
@@ -191,3 +191,8 @@ def _make_or_mod_server_block(self, achall):
 
         self.configurator.parser.add_server_directives(vhost,
             location_directive, replace=False)
+
+        rewrite_directive = [['rewrite', ' ', '^(/.well-known/acme-challenge/.*)',
+                                ' ', '$1', ' ', 'break']]
+        self.configurator.parser.add_server_directives(vhost,
+            rewrite_directive, replace=False, insert_at_top=True)
diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py
index 5497f7e6360..fbd6c0ade8c 100644
--- a/certbot-nginx/certbot_nginx/parser.py
+++ b/certbot-nginx/certbot_nginx/parser.py
@@ -276,7 +276,7 @@ def has_ssl_on_directive(self, vhost):
 
         return False
 
-    def add_server_directives(self, vhost, directives, replace):
+    def add_server_directives(self, vhost, directives, replace, insert_at_top=False):
         """Add or replace directives in the server block identified by vhost.
 
         This method modifies vhost to be fully consistent with the new directives.
@@ -293,10 +293,12 @@ def add_server_directives(self, vhost, directives, replace):
             whose information we use to match on
         :param list directives: The directives to add
         :param bool replace: Whether to only replace existing directives
+        :param bool insert_at_top: True if the directives need to be inserted at the top
+            of the server block instead of the bottom
 
         """
         self._modify_server_directives(vhost,
-            functools.partial(_add_directives, directives, replace))
+            functools.partial(_add_directives, directives, replace, insert_at_top))
 
     def remove_server_directives(self, vhost, directive_name, match_func=None):
         """Remove all directives of type directive_name.
@@ -521,7 +523,7 @@ def _is_ssl_on_directive(entry):
             len(entry) == 2 and entry[0] == 'ssl' and
             entry[1] == 'on')
 
-def _add_directives(directives, replace, block):
+def _add_directives(directives, replace, insert_at_top, block):
     """Adds or replaces directives in a config block.
 
     When replace=False, it's an error to try and add a nonrepeatable directive that already
@@ -535,17 +537,18 @@ def _add_directives(directives, replace, block):
 
     :param list directives: The new directives.
     :param bool replace: Described above.
+    :param bool insert_at_top: Described above.
     :param list block: The block to replace in
 
     """
     for directive in directives:
-        _add_directive(block, directive, replace)
+        _add_directive(block, directive, replace, insert_at_top)
     if block and '\n' not in block[-1]:  # could be "   \n  " or ["\n"] !
         block.append(nginxparser.UnspacedList('\n'))
 
 
 INCLUDE = 'include'
-REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE, 'location'])
+REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE, 'location', 'rewrite'])
 COMMENT = ' managed by Certbot'
 COMMENT_BLOCK = [' ', '#', COMMENT]
 
@@ -597,7 +600,7 @@ def _find_location(block, directive_name, match_func=None):
     return next((index for index, line in enumerate(block) \
         if line and line[0] == directive_name and (match_func is None or match_func(line))), None)
 
-def _add_directive(block, directive, replace):
+def _add_directive(block, directive, replace, insert_at_top):
     """Adds or replaces a single directive in a config block.
 
     See _add_directives for more documentation.
@@ -619,7 +622,7 @@ def is_whitespace_or_comment(directive):
             block[location] = directive
             comment_directive(block, location)
             return
-    # Append directive. Fail if the name is not a repeatable directive name,
+    # Append or prepend directive. Fail if the name is not a repeatable directive name,
     # and there is already a copy of that directive with a different value
     # in the config file.
 
@@ -652,8 +655,15 @@ def can_append(loc, dir_name):
                     _comment_out_directive(block, included_dir_loc, directive[1])
 
     if can_append(location, directive_name):
-        block.append(directive)
-        comment_directive(block, len(block) - 1)
+        if insert_at_top:
+            # Add a newline so the comment doesn't comment
+            # out existing directives
+            block.insert(0, nginxparser.UnspacedList('\n'))
+            block.insert(0, directive)
+            comment_directive(block, 0)
+        else:
+            block.append(directive)
+            comment_directive(block, len(block) - 1)
     elif block[location] != directive:
         raise errors.MisconfigurationError(err_fmt.format(directive, block[location]))
 

Part of #5409.

Implementation as suggested by https://community.letsencrypt.org/t/help-test-certbot-apache-and-nginx-fixes-for-tls-sni-01-outage/50207/18.

Implementation currently adds a redundant `rewrite` directive every time we add a `location` block to the server block to handle a different challenge. This is not a problem, especially since this code doesn't need to be nice to look at, it just needs to be temporarily understandable by Nginx to serve the responses. On the principle of doing the minimally invasive code changes on this short notice, I contend that it's better to leave this than to write more code to dedupe the addition.