Loosing head over nginx
Loosing Header over nginx
In my app I serve a booking form - newbooking.html
- , after sending it I want to present the user a detail.html
page, showing the content of the just sent booking in a table and under the table buttons. The first button should always be visible, it’s
there to send the admins a message concerning the booking (users can visit the detail page for all their bookings, not just a recently sent one). The second one should only be there if the user got redirected to the detail.html
page from the newbooking.html
page; so after submitting the newbooking
form. This button simply links to the newbooking.html
page and says something like “send another booking”
Initial solution - request.referrer
My initial solution was to check for the request.referrer
in the jinja2
template and if it matches my domain and the path for newbooking.html
, show the second button.
So in my routes I check if I have a validate_on_submit()
do stuff to the form data and the redirect
to the detail.html
page passing along the dynamic uuid.
routes.py
1@bp.route('/newbooking', methods=['GET', 'POST'])
2def newbooking():
3
4 form=BookingForm()
5
6 if form.validate_on_submit():
7 uuid = generate('123456789ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz', 8)
8 #do stuff with the data from the form
9
10
11 flash('You just sent us the following booking:'
12 return redirect(url_for('detail', buuid=uuid))
jinja2 template
1{% extends 'base.html' %}
2
3{% block app_content %}
4<h1>Details for Booking: {{ buuid.booking_uuid }}</h1>
5<div class="row">
6 <div class="table-responsive">
7 <table class="table table-condensed table-hover">
8 <tr>
9 <td>BOOKING CONTENT HERE</td>
10 </tr>
11 </table>
12
13 {% if request.referrer != None and request.referrer[-11:] == "/newbooking" %}
14 <tr>
15 <td colspan="2"><a href="{{ url_for('bookingmsg', buuid=buuid.booking_uuid) }}"><button type="button" class="btn btn-warning" style="width:100%; margin-bottom: 15px;">send us a message for this booking</button></a></td>
16 </tr>
17 <tr>
18 <td colspan="2"><a href="{{ url_for('newbooking') }}"><button type="button" class="btn btn-success" style="width:100%">make a new booking</button></a></td>
19 </tr>
20
21 {% else %}
22 <tr>
23 <td colspan="2"><a href="{{ url_for('bookingmsg', buuid=buuid.booking_uuid) }}"><button type="button" class="btn btn-warning " style="width:100%; margin-bottom: 15px;">send us a message for this booking</button></a></td>
24 </tr>
25 {% endif %}
26
27 </div>
28
29</div>
30
31
32{% endblock app_content %}
This worked fine on my local setup. The client receives the request.referrer
(one can check it in the developer console with document.referrer
).
So I happily pushed to production.
It stopped working.
Production server problems
My production server is a small AWS Lightsail instance running debian and service my flask app via gunicorn and a nginx reverse proxy.
1server {
2
3listen 443 ssl;
4
5server_name app.info
6
7
8access_log /var/log/access.log;
9
10error_log /var/log/error.log;
11
12
13location / {
14
15proxy_pass http://localhost:8000;
16
17proxy_redirect off;
18
19proxy_set_header Host $host;
20
21proxy_set_header X-Real-IP $remote_addr;
22
23proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
24
25}
26
27ssl_certificate path/to/cert
28
29ssl_certificate_key /path/to/certkey
30
31}
Surfing on my app the referrer normally showed up fine: app.info/whateverpage
.
BUT after submitting a form and getting redirected to the detail.html
page left the referrer empty. So obviusly my jinja
template never showed the make a new booking
button.
My guess was that there is a problem between the nginx reverse proxy and gunicorn, which removes the referrer, when landing on a page after a flask redirect
, since it worked fine after flask render_template
.
I tried building my own responses and including the referrer in the header - to no avail.
I was ready to give up. I already spent several hours making this button appear in production.
Then I found a workaround!
request.args to the rescue
I appened a parameter/argument to the flask redirect
after submitting the form!
All this does it append ?redirected=True
to the url. So hitting detail.html
after the redirect shows up as https://app.info/detail/<buuid>?redirected=True
So my updated routes.py
:
1@bp.route('/newbooking', methods=['GET', 'POST'])
2def newbooking():
3
4 form=BookingForm()
5
6 if form.validate_on_submit():
7 uuid = generate('123456789ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz', 8)
8 #do stuff with the data from the form
9
10
11 flash('You just sent us the following booking:'
12 return redirect(url_for('detail', buuid=uuid, **redirected=True**)) #added parameter here!
updated part of the jinja2
template for detail.html
:
1{% if **request.args.get('redirected') == "True"** %}
2 <tr>
3 <td colspan="2"><a href="{{ url_for('bookingmsg', buuid=buuid.booking_uuid) }}"><button type="button" class="btn btn-warning" style="width:100%; margin-bottom: 15px;">send us a message for this booking</button></a></td>
4 </tr>
5 <tr>
6 <td colspan="2"><a href="{{ url_for('newbooking') }}"><button type="button" class="btn btn-success" style="width:100%">make a new booking</button></a></td>
7 </tr>
8
9 {% else %}
10 <tr>
11 <td colspan="2"><a href="{{ url_for('bookingmsg', buuid=buuid.booking_uuid) }}"><button type="button" class="btn btn-warning " style="width:100%; margin-bottom: 15px;">send us a message for this booking</button></a></td>
12 </tr>
13 {% endif %}
The only caveat is that users can manually append ?redirected=True
to their url. However, since the only functionality is to show or not show a button linking to a page they find in any way also in their navbar, I don’t see any problem (please correct me if I’m wrong)