Proxying Web Servers
Preview
Positron Run App
Positron provides a simplified method for running interactive apps within the IDE. We recommend using Positron’s Run App feature instead of running an app from the Terminal yourself. See Positron’s Run interactive apps documentation for more information on how to use the feature.
Before running your app in a Positron Pro session, please make the applicable framework-specific modifications to your application to ensure that the app can be proxied appropriately. The Workbench Extension supports most of the proxied servers created by the Run App feature.
Dash
The Positron Run App feature manages path-related Dash environment variables on your behalf. Therefore, avoid setting DASH_URL_BASE_PATHNAME
, DASH_REQUESTS_PREFIX_PATHNAME
, DASH_ROUTES_PATHNAME_PREFIX
, and other Dash environment variables which modify the application path.
Dash applications started from the Positron Run App feature are not supported in the Proxied Servers view in the Workbench Extension. To utilize the Proxied Servers view, run the Dash application from the Terminal. See the Running apps in the Terminal section below for more information.
If you’ve previously opened your project directory in a VS Code session in Workbench, delete the generated .env
file or remove the PORT
and DASH_REQUESTS_PATHNAME_PREFIX
variables from your .env
file to avoid conflicts with Positron’s Run App feature.
FastAPI
When using the Uvicorn ASGI server, Workbench sets the environment variable UVICORN_ROOT_PATH
to the necessary value for the default FastAPI port 8000 in Positron Pro sessions. Therefore, avoid setting the UVICORN_ROOT_PATH
environment variable.
Positron displays the interactive API documentation in the Viewer.
Flask
Since Workbench runs an Nginx HTTP proxy server, Flask applications must include additional logic to tell Flask it is running behind a proxy. Flask recommends using the Werkzeug middleware to do this. From Werkzeug:
Middlewares wrap applications to dispatch between them or provide additional request handling
This module provides a middleware that adjusts the WSGI environ based on
X-Forwarded-
headers that proxies in front of an application may set. When an application is running behind a proxy server, WSGI may see the request as coming from that server rather than the real client. Proxies set various headers to track where the request actually came from. This middleware should only be used if the application is actually behind such a proxy, and should be configured with the number of proxies that are chained in front of it. Not all proxies set all the headers. Since incoming headers can be faked, you must set how many proxies are setting each header so the middleware knows what to trust.
The Workbench Extension provides the following Posit Workbench Flask Interface code snippet so that users can quickly access this middleware:
from flask import Flask
import os
= Flask(__name__)
app if 'RS_SERVER_URL' in os.environ and os.environ['RS_SERVER_URL']:
from werkzeug.middleware.proxy_fix import ProxyFix
= ProxyFix(app.wsgi_app, x_prefix=1) app.wsgi_app
Trigger this snippet by typing from flask import
within a python file.
The line if 'RS_SERVER_URL' in os.environ and os.environ['RS_SERVER_URL']:
tests if the RS_SERVER_URL
environment variable is set before implementing the middleware fix and ensures that this middleware only runs when running the application from within Workbench.
Running apps in the Terminal
We recommend using Positron’s Run App feature to run your app because it simplifies the application proxying process. See Positron Run App for more information.
The Posit Workbench Extension includes a Proxied Servers view when selecting the extension from the Activity Bar. This view contains a list of currently running web servers. Each item in this list includes the web server’s name and the port the process is running on. Selecting an item opens the server in a new browser tab.
The extension determines which servers to display by searching through your currently running processes for those with open and listening sockets. The extension excludes processes it expects and non-development servers such as R and Jupyter sessions. For R and Python processes, the extension represents the server’s name as the directory name.
We have ensured the following server types can be proxied when using the extension in Positron Pro sessions (other application types may work as expected):
Dash
Requirements
- Minimum Dash version of 1.10.0 (earlier versions require the user to manually set the
port
variable in therun
call) - The server must be run with the debug argument set to
True
e.g.,app.run(debug=True)
- The
python-dotenv
package – can be installed withpip install python-dotenv
Environment variables
The following environment variables must be exported in the Terminal or set in a .env
file before running the Dash app:
PORT
DASH_REQUESTS_PREFIX_PATHNAME
In the Terminal, run the following commands to get the proxied server pathname and to set the environment variables:
Terminal
# Export the port number, such as 8050
export PORT=8050
# Use the rserver-url binary to get the proxied server address
/usr/lib/rstudio-server/bin/rserver-url -l $PORT
# Example output for /usr/lib/rstudio-server/bin/rserver-url -l $PORT:
# https://workbench-server/s/0976a6ae9dc1b15c17c63/p/4624f968/
# Export the DASH_REQUESTS_PREFIX_PATHNAME variable with the path part
# of the output from the rserver-url command, such as
# /s/0976a6ae9dc1b15c17c63/p/4624f968/
export DASH_REQUESTS_PREFIX_PATHNAME=/s/0976a6ae9dc1b15c17c63/p/4624f968/
Alternatively, set the environment variables in a .env
file in the root of your project directory. For example:
.env
PORT=8050
DASH_REQUESTS_PREFIX_PATHNAME=/s/0976a6ae9dc1b15c17c63/p/4624f968/
Then, proceed to run your Dash app in the Terminal.
Dash doesn’t always respect the port provided by python-dotenv
, even when it is properly reading other variables from the file, such as DASH_REQUESTS_PREFIX_PATHNAME
. Posit is looking into a long-term fix for this issue, but there are manual steps that can be taken to force Dash to use the provided port.
One way to force Dash to use the provided port is to run your code using the Python extension and Positron’s debugger. Alternatively, update your main application file to load the python-dotenv
file before importing Dash, as in the following:
from dotenv import load_dotenv
load_dotenv()import dash
Manually setting a port number
To force Workbench to use a specific port, set the desired port in the run
call or add a comment to the top of the main app file containing the port number in the following format:
# .run(port = 'your_port_num_here').
FastAPI
Since Workbench sessions run remotely behind a proxy, FastAPI applications on Workbench must use the ASGI root_path
mechanism. For more information, see FastAPI’s Behind a Proxy documentation.
The following instructions describe how to set the appropriate application port and root path for an app run with the Uvicorn ASGI server. Uvicorn is the default Asynchronous Server Gateway Interface for FastAPI applications.
Using the default port
When using the default FastAPI port, 8000, no additional configuration is required by the user. This is because Workbench sets the environment variable UVICORN_ROOT_PATH
to the necessary value for port 8000 in all Positron Pro sessions.
However, we recommend specifying the port
and root_path
directly instead of falling back on the default port.
Using a specific port
and root_path
To specify a port, use one of the following methods:
- Start Uvicorn in the Terminal and pass the desired port and root path as command line arguments
- Import the Uvicorn module into your Python code and pass a port to the
uvicorn.run
function
The examples in this section use port 8050. This value should be replaced with your desired port.
To determine the root path, use the rserver-url
binary as shown in the following examples. See URL Prefixes page for more information.
Specify a port and root path in the Terminal
When specifying a port that is not 8000, such as port 8050, users must pass the root-path
argument to Uvicorn. Use the rserver-url
binary with the -l
flag to set the root_path
variable. This looks like the following:
uvicorn main:app --port=8050 --root-path=$(/usr/lib/rstudio-server/bin/rserver-url -l 8050)
If this root path is not specified and a port has been passed to Uvicorn, the server displays in the Proxied Servers view with a warning:
Access the server by clicking on it as usual, but all features may not work as expected.
When accessing a running FastAPI application, it typically displays a JSON response by default. To get to the interactive API documentation, add /docs
or /redoc
to the end of the URL. For more information, see the FastAPI: User Guide.
Import the Uvicorn module
Users can programmatically specify a port to run their application on by passing the port number to the Uvicorn constructor. Typically, this would look like the following code:
from fastapi import FastAPI
import uvicorn
= FastAPI()
app
if __name__ == '__main__':
= 8050) uvicorn.run(app, port
When running in Workbench, users should call the rserver-url
binary with the -l
flag to set the root_path
variable. Posit Workbench provides the Posit Workbench FastAPI Uvicorn Root Path code snippet for users to easily retrieve this required code. This snippet can be retrieved by typing from fastapi import FastAPI
:
from fastapi import FastAPI
import uvicorn
= FastAPI()
app
if __name__ == '__main__':
= '', 8050
path, port
if 'RS_SERVER_URL' in os.environ and os.environ['RS_SERVER_URL']:
= subprocess.run(f'echo $(/usr/lib/rstudio-server/bin/rserver-url -l {port})',
path =subprocess.PIPE, shell=True).stdout.decode().strip()
stdout= port, root_path = path) uvicorn.run(app, port
The path
variable is set to the URL prefix for the current Workbench session and the requested port. It will look like the URL that your Positron Pro session is running at but with an additional /p/<port-id>
suffix. Because this value will vary per session, it should not be hard-coded. Always use rserver-url
to generate the value.
The path
variable is only set when the environment variable RS_SERVER_URL
is set. This ensures that the path
variable is not set when this code is run outside of Workbench. If you want to configure path
to a different value when run outside of Workbench, replace the empty string that path
is initialized to with your desired path:
= '<default-path>', 8050 path, port
Flask
Since Workbench runs an Nginx HTTP proxy server, Flask applications must include additional logic to tell Flask it is running behind a proxy. Flask recommends using the Werkzeug middleware to do this. From Werkzeug:
Middlewares wrap applications to dispatch between them or provide additional request handling
This module provides a middleware that adjusts the WSGI environ based on
X-Forwarded-
headers that proxies in front of an application may set. When an application is running behind a proxy server, WSGI may see the request as coming from that server rather than the real client. Proxies set various headers to track where the request actually came from. This middleware should only be used if the application is actually behind such a proxy, and should be configured with the number of proxies that are chained in front of it. Not all proxies set all the headers. Since incoming headers can be faked, you must set how many proxies are setting each header so the middleware knows what to trust.
The Workbench Extension provides the following Posit Workbench Flask Interface code snippet so that users can quickly access this middleware:
from flask import Flask
import os
= Flask(__name__)
app if 'RS_SERVER_URL' in os.environ and os.environ['RS_SERVER_URL']:
from werkzeug.middleware.proxy_fix import ProxyFix
= ProxyFix(app.wsgi_app, x_prefix=1) app.wsgi_app
Trigger this snippet by typing from flask import
within a python file.
The line if 'RS_SERVER_URL' in os.environ and os.environ['RS_SERVER_URL']:
tests if the RS_SERVER_URL
environment variable is set before implementing the middleware fix and ensures that this middleware only runs when running the application from within Workbench.