Pat
Pat

Reputation: 3

Flask/Python/Gunicorn/Nginx WebApp dropping https from target web page when submitting Flask WTForm

this is my first ever time submitting on StackOverflow, I hope I provide enough details.

I have been building a Python/Flask web app, based loosely on the framework used in the blog by Miguel Grinberg

It works fine in my PyCharm IDE, the issue is when I deployed it to an ubuntu server on Oracle VirtualBox using Gunicorn and NginX on top of the Flask server.

The webapp consists of 10 pages/templates and when deployed and running on Virtulbox, I can navigate around the app fine on my host browser UNTIL I submit a FlaskForm which then should return a RESULTS page with the required data.

It is supposed to return https://127.0.0.1:3000/results but instead returns only http: http://127.0.0.1:3000/results

(https dropped to http)

and the resulting

400 Bad Request

The plain HTTP request was sent to HTTPS port

nginx/1.18.0 (Ubuntu)

The error is self explanatory and I know somewhere it may be in the nginx config somewhere I just don't have the knowledge (been trying to nut this out for a week or so)

 /etc/nginx/sites-enabled/highceesdev:

server {
# listen on port 80 (http)
listen 80;
server_name _;
location / {
    # redirect any requests to the same URL but on https
    return 301 https://$host$request_uri;
}

}

server {
# listen on port 443 (https)
listen 443 ssl;
server_name _;

# location of the self-signed SSL certificate
ssl_certificate /home/ubuntu2/HighCees/HighCeesDev/certs/cert.pem;
ssl_certificate_key /home/ubuntu2/HighCees/HighCeesDev/certs/key.pem;

# write access and error logs to /var/log
access_log /var/log/highceesdev_access.log;
error_log /var/log/highceesdev_error.log;

location / {
    # forward application requests to the gunicorn server
    proxy_pass http://localhost:8000;
    proxy_redirect off;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

location /static {
    # handle static files directly, without forwarding to the application
    alias /home/ubuntu2/HighCees/HighCeesDev/app/static;
    expires 30d;
}

}

If relevant, further code as follows.

The definition of the FlaskForm being submitted is HighCeesDev/app/forms.py:

....
class CreateSong2(FlaskForm):
startdate = DateField('Start Date', default=(datetime.today()-timedelta(90)),format='%Y-%m-%d',validators=(validators.data_required(),))
enddate = DateField('End Date', default=datetime.today(),format='%Y-%m-%d', validators=(validators.data_required(),))
city = RadioField(choices=[('Wellington', 'Wellington'), ('Auckland', 'Auckland'),('Christchurch', 'Christchurch'), ('Dunedin', 'Dunedin')])
submit = SubmitField('Submit')

...

The definition of the routes for CreateSong2(FlaskForm) and results in HighCeesDev/app/routes.py:

@app.route('/create_song2',methods=['GET','POST'])
def create_song2():
latedate = Song.query.order_by(Song.date.desc()).first()
startdate = Song.query.order_by(Song.date.asc()).first()
convertedlatedate1=latedate.date
convertedlatedate2 = convertedlatedate1.strftime("%d/%m/%Y")
convertedstartdate1=startdate.date
convertedstartdate2 = convertedstartdate1.strftime("%d/%m/%Y")
form = CreateSong2()
if form.validate_on_submit():
    session['startdate'] = form.startdate.data
    session['enddate'] = form.enddate.data
    session['shcity'] = form.city.data
    shcity = form.city.data
    purge_files()
    return redirect(url_for('results'))
return render_template('create_song2.html',form=form, latedate=latedate, startdate=startdate,convertedlatedate2=convertedlatedate2, convertedstartdate2=convertedstartdate2)

and

@app.route('/results',methods=['GET','POST'])
def results():
startdate = session['startdate']
endate= session['enddate']
shcity = session['shcity']
form=Results()
startedate = extract_date_sequel(startdate)
finishedate = extract_date_sequel(endate)
song = Song.query.filter(Song.date <= finishedate,Song.date >= startedate,Song.city==shcity).order_by(Song.date.asc())
song2 = Song.query.with_entities(Song.high_temp).filter(Song.date <= finishedate,Song.date >= startedate,Song.city==shcity).order_by(Song.date.asc())
if not check_song_query(song2):
    print("check_song_query says its False :-(!!")
    flash("No data in that query :-(")
    return redirect(url_for('create_song2'))
else:
    print("check_song_query says its True!!")
print("song is: ", type(song))
print("song2 is: ",type(song2))
a = []
for i in song2:
    stringy = ''
    stringy = stringy.join(i._data)
    a.append(stringy)
print(type(a))
print(a)
songfile = SongFileEvent.query.all()
dtf = date_time_file_wav()
record = SongFileEvent(song_file_name=dtf)
db.session.add(record)
db.session.commit()
testy2(a)
daft = SongFileEvent.query.order_by(SongFileEvent.id.desc()).first()
songfile = SongFileEvent.query.all()
now = time.time()
purge_files_2(now)
print("purge files was called")
#convert_numbers_to_notes(a)
return render_template('results.html',title='Home', song=song, songfile=songfile, daft=daft)

Keen to offer more details if that helps.

Thanks a lot

Pat

Upvotes: 0

Views: 433

Answers (1)

pjcunningham
pjcunningham

Reputation: 8046

In your deployment your view is running within the context of a http request - http://localhost:8000 and url_for will return URLs with the 'http' protocol.

You need to set up the Flask instance with specific configuration settings. I find the following two settings fix these kind of issues:

PREFERRED_URL_SCHEME

SERVER_NAME

A simple example.

# app/config.py

class Config(object):
    # common configurations
    SECRET_KEY = 'XXXXXXX'
    MAX_CONTENT_LENGTH = 32 * 1024 * 1024

    ### more settings


class DevelopmentConfig(Config):
    # specific configurations
    SERVER_NAME = "example.local:5072"
    PREFERRED_URL_SCHEME = 'http'

    ### more settings


class ProductionConfig(Config):
    # specific configurations
    SERVER_NAME = "example.com"
    PREFERRED_URL_SCHEME = 'https'

    ### more settings


# app/__init__.py

def create_app():
    app = App(__name__)
    
    # APP_SETTINGS is an environment variable either set to:
    # export APP_SETTINGS=app.config.DevelopmentConfig
    # or
    # export APP_SETTINGS=app.config.ProductionConfig

    app.config.from_object(os.environ['APP_SETTINGS'])

    ### more setup

    return app

Nginx Proxy Settings

location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_redirect off;
    proxy_pass http://localhost:8000;
    proxy_set_header X-Sendfile-Type X-Accel-Redirect;
}

Upvotes: 0

Related Questions