Delegate management of canonical URLs to Apache

This middleware is mostly redundant:

 * redirecting to canonical URLs can be done more simply in Apache
 * appending a forward slash is a default in CommonMiddleware now
 * we're no longer using Squid cache

May need to update Apache to strip/redirect trailing "index.html".
This commit is contained in:
Ben Sturmfels 2024-03-20 14:54:54 +11:00
parent c795e1799c
commit 5fa226284b
Signed by: bsturmfels
GPG key ID: 023C05E2C9C068F0
8 changed files with 4 additions and 85 deletions

View file

@ -1,6 +1,6 @@
# To-do # To-do
* remove `ForceCanonicalHostnameMiddleware` by ensuring canonical redirect and HTTPS redirect is done by Apache * ask Denver about why so many license files
* serve a 400 in Apache for a hostname we don't explicitly support * serve a 400 in Apache for a hostname we don't explicitly support
* use `<detail>` elements for supporter page hidden sections, rather than complex jQuery - or consider Alpine.js * use `<detail>` elements for supporter page hidden sections, rather than complex jQuery - or consider Alpine.js
* replace `internalNavigate` with inline flexbox layout * replace `internalNavigate` with inline flexbox layout
@ -9,6 +9,7 @@
# Done # Done
* remove `ForceCanonicalHostnameMiddleware` by ensuring canonical redirect and HTTPS redirect is done by Apache
* standardise settings to replace `settings.py` and `djangocommonsettings.py` * standardise settings to replace `settings.py` and `djangocommonsettings.py`
with `settings/prod.py` and move `SECRET_KEY` to an environment variable with `settings/prod.py` and move `SECRET_KEY` to an environment variable
* migrate to Django 4.2 LTS * migrate to Django 4.2 LTS

View file

@ -114,11 +114,8 @@ def query(request):
def relative_redirect(request, path): def relative_redirect(request, path):
from django import http from django import http
from django.conf import settings
host = request.get_host() host = request.get_host()
if settings.FORCE_CANONICAL_HOSTNAME:
host = settings.FORCE_CANONICAL_HOSTNAME
url = "{}://{}{}".format(request.is_secure() and 'https' or 'http', host, path) url = "{}://{}{}".format(request.is_secure() and 'https' or 'http', host, path)
return http.HttpResponseRedirect(url) return http.HttpResponseRedirect(url)

View file

@ -1,7 +1,5 @@
from datetime import datetime as DateTime from datetime import datetime as DateTime
from django.conf import settings
from .fundgoal.models import FundraisingGoal from .fundgoal.models import FundraisingGoal
SITE_FUNDGOAL = 'cy2023-end-year-match' SITE_FUNDGOAL = 'cy2023-end-year-match'
@ -19,10 +17,5 @@ def sitefundraiser(request):
'sitefundgoal': fundgoal_lookup(SITE_FUNDGOAL), 'sitefundgoal': fundgoal_lookup(SITE_FUNDGOAL),
} }
if settings.FORCE_CANONICAL_HOSTNAME:
_HOST_URL_VAR = {'host_url': 'https://' + settings.FORCE_CANONICAL_HOSTNAME}
def host_url(request):
return _HOST_URL_VAR
else:
def host_url(request): def host_url(request):
return {'host_url': request.build_absolute_uri('/').rstrip('/')} return {'host_url': request.build_absolute_uri('/').rstrip('/')}

View file

@ -1,60 +0,0 @@
from django import http
from django.conf import settings
from django.utils.cache import patch_response_headers
from django.utils.deprecation import MiddlewareMixin
class ForceCanonicalHostnameMiddleware(MiddlewareMixin):
# MiddlewareMixin provides compatiiblity for Django 1.10 style middleware.
def process_request(self, request):
"""Modified common middleware for Conservancy site
* Performs redirects to strip trailing "index.html"
* performs redirects based on APPEND_SLASH
* performs redirects based on site-specific REDIRECT_TABLE
* adds cache headers to provide hints to squid
"""
# Never allow connection to the /admin part of the site without SSL
if (not request.is_secure) and request.path.startswith('/admin'):
url = 'https://sfconservancy.org%s' % request.path
return http.HttpResponseRedirect(url)
# Check for a redirect based on settings.APPEND_SLASH
host = request.get_host()
old_url = [host, request.path]
new_url = old_url[:]
# Append a slash if append_slash is set and the URL doesn't have a
# trailing slash or a file extension.
if settings.APPEND_SLASH and (old_url[1][-1] != '/') and ('.' not in old_url[1].split('/')[-1]):
new_url[1] = new_url[1] + '/'
if settings.DEBUG and request.method == 'POST':
raise(RuntimeError, "You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to {}{} (note the trailing slash), or set APPEND_SLASH=False in your Django settings.".format(new_url[0], new_url[1]))
# Strip trailing index.html
if new_url[1].endswith('/index.html'):
new_url[1] = new_url[1][:new_url[1].rfind('index.html')]
# Consult redirect table (if exists)
if hasattr(settings, "REDIRECT_TABLE"):
if new_url[1] in settings.REDIRECT_TABLE:
new_url[1] = settings.REDIRECT_TABLE[new_url[1]]
if new_url != old_url:
# Force canonical hostname
if settings.FORCE_CANONICAL_HOSTNAME:
new_url[0] = settings.FORCE_CANONICAL_HOSTNAME
# Redirect
if new_url[0]:
newurl = "{}://{}{}".format(request.is_secure() and 'https' or 'http', new_url[0], new_url[1])
else:
newurl = new_url[1]
if request.GET:
newurl += '?' + request.GET.urlencode()
return http.HttpResponseRedirect(newurl)
return None
def process_response(self, request, response):
# provide hints to squid
if request.method in ('GET', 'HEAD') and response.status_code == 200:
patch_response_headers(response)
return response

View file

@ -105,11 +105,8 @@ def query(request):
def relative_redirect(request, path): def relative_redirect(request, path):
from django import http from django import http
from django.conf import settings
host = http.get_host(request) host = http.get_host(request)
if settings.FORCE_CANONICAL_HOSTNAME:
host = settings.FORCE_CANONICAL_HOSTNAME
url = "%s://%s%s" % (request.is_secure() and 'https' or 'http', host, path) url = "%s://%s%s" % (request.is_secure() and 'https' or 'http', host, path)
return http.HttpResponseRedirect(url) return http.HttpResponseRedirect(url)

View file

@ -22,10 +22,6 @@ from pathlib import Path
SITE_ID = 2 SITE_ID = 2
ROOT_URLCONF = 'conservancy.urls' ROOT_URLCONF = 'conservancy.urls'
REDIRECT_TABLE = {
'www.sf-conservancy.org': 'sfconservancy.org',
}
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': False, 'disable_existing_loggers': False,
@ -136,7 +132,6 @@ MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'conservancy.middleware.ForceCanonicalHostnameMiddleware',
] ]
USETHESOURCE = { USETHESOURCE = {

View file

@ -3,8 +3,6 @@ from .base import *
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
FORCE_CANONICAL_HOSTNAME = False
DATABASES = { DATABASES = {
'default': { 'default': {
'NAME': 'conservancy-website.sqlite3', 'NAME': 'conservancy-website.sqlite3',

View file

@ -7,8 +7,6 @@ from .base import *
DEBUG = False DEBUG = False
ALLOWED_HOSTS = ['www.sfconservancy.org', 'sfconservancy.org'] ALLOWED_HOSTS = ['www.sfconservancy.org', 'sfconservancy.org']
FORCE_CANONICAL_HOSTNAME = 'sfconservancy.org'
ADMINS = [ ADMINS = [
('Bradley M. Kuhn', 'sysadmin@sfconservancy.org'), ('Bradley M. Kuhn', 'sysadmin@sfconservancy.org'),
('Ben Sturmfels', 'sysadmin+conservancy@sturm.com.au'), ('Ben Sturmfels', 'sysadmin+conservancy@sturm.com.au'),