-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
How to put Etherpad Lite behind a reverse Proxy
Feel free to add your own config examples.
If you want a secure SSL connection to your Etherpad, you can also use a native Node https server, see https://github.com/ether/etherpad-lite/pull/1202 .
Creating Certificate Authorities and self-signed SSL certificates: http://web.archive.org/web/20110704035103/http://www.tc.umn.edu/~brams006/selfsign.html
Accept https://
encrypted traffic and force http://
to https://
Simple proxy to accept https traffic and proxy to port 9001
and redirect http://
traffic dynamically to become https
.
Includes default paths to letsencrypt cert files (created by running certbot certonly --standalone -d yourdomain.com
after installing certbot).
const fs = require('fs');
const http = require('http');
const httpProxy = require('http-proxy');
//
// Create a HTTP proxy server
//
const targetPort = 9001;
httpProxy.createServer({
target: {
host: 'localhost',
port: targetPort,
},
ssl: {
key: fs.readFileSync('/etc/letsencrypt/live/yourdomain.com/privkey.pem', 'utf8'),
cert: fs.readFileSync('/etc/letsencrypt/live/yourdomain.com/cert.pem', 'utf8'),
},
}).listen(443);
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(80);
console.log('Now proxying port 443 → http://0.0.0.0:'+targetPort +' and redirecting port 80 to port 443');
Simple example proxy server implemented in node.js with node-http-proxy. This example proxies on port 80 and 443. The port 80 proxy proxies to port 81 (where Apache is listening), and the port 443 proxy connects to Etherpad on port 9001.
var fs = require('fs'),
http = require('http'),
https = require('https'),
httpProxy = require('http-proxy');
//
// Create a HTTP proxy server
//
var regular_proxy = httpProxy.createServer(81, 'localhost').listen(80);
var routes_json = {
"server.org/pad": "127.0.0.1:9001",
".*": "127.0.0.1:81"
};
//
// Create a HTTPS proxy server
//
var ssl_proxy = httpProxy.createServer({
router: routes_json,
https: {
key: fs.readFileSync('/etc/apache2/ssl/server.org.key', 'utf8'),
cert: fs.readFileSync('/etc/apache2/ssl/server.org.crt', 'utf8'),
ca: fs.readFileSync('/etc/apache2/ssl/gd_bundle.crt', 'utf8')
},
}).listen(443);
General Apache security advice [added 2011-11-26]:
Apache users should carefully check their version, and generally check their conf files for a potential security issue as described in http://www.h-online.com/open/news/item/Apache-patch-patches-poorly-1385107.html which could allow access to internal systems if the reverse proxy rules are configured incorrectly. The following configuration appears to be correct and safe, as the proxy statements ends with
a closing "/".
To implement these configs place them in your Apache configuration folder, usually /etc/httpd/conf/ -- If this is new to you then you should read up on Apache virtual hosts and configuration. You will need to activate (maybe after having installed) the mod_proxy, mod_proxy_http, mod_headers, proxy_wstunnel, mod_deflate and mod_rewrite Apache modules.
<VirtualHost *:80>
ServerName etherpad.domain.org
ProxyVia On
ProxyRequests Off
ProxyPreserveHost on
<Location />
ProxyPass http://localhost:9001/ retry=0 timeout=30
ProxyPassReverse http://localhost:9001/
</Location>
<Location /socket.io>
# This is needed to handle the websocket transport through the proxy, since
# etherpad does not use a specific sub-folder, such as /ws/ to handle this kind of traffic.
# Taken from https://github.com/ether/etherpad-lite/issues/2318#issuecomment-63548542
# Thanks to beaugunderson for the semantics
RewriteEngine On
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:9001/socket.io/$1 [P,L]
ProxyPass http://localhost:9001/socket.io retry=0 timeout=30
ProxyPassReverse http://localhost:9001/socket.io
</Location>
<Proxy *>
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Proxy>
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName etherpad.domain.org
# SSL configuration
SSLEngine on
# If you hold wildcard certificates for your domain the next two lines are not necessary.
SSLCertificateFile "/path/to/etherpad.domain.org/certificate.pem"
SSLCertificateKeyFile "/path/to/etherpad.domain.org/privatekey.pem"
ProxyVia On
ProxyRequests Off
ProxyPreserveHost on
<Location />
AuthType Basic
AuthName "Welcome to the domain.org Etherpad"
AuthUserFile /path/to/svn.passwd
AuthGroupFile /path/to/svn.group
Require group etherpad
ProxyPass http://localhost:9001/ retry=0 timeout=30
ProxyPassReverse http://localhost:9001/
</Location>
<Location /socket.io>
# This is needed to handle the websocket transport through the proxy, since
# etherpad does not use a specific sub-folder, such as /ws/ to handle this kind of traffic.
# Taken from https://github.com/ether/etherpad-lite/issues/2318#issuecomment-63548542
# Thanks to beaugunderson for the semantics
RewriteEngine On
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:9001/socket.io/$1 [P,L]
ProxyPass http://localhost:9001/socket.io retry=0 timeout=30
ProxyPassReverse http://localhost:9001/socket.io
</Location>
<Proxy *>
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Proxy>
</VirtualHost>
</IfModule>
Add this to allow nice urls, such as "https://etherpad.example.org/padname"
RewriteEngine On
RewriteRule /p/*$ https://etherpad.example.org/ [NC,L]
RewriteCond %{REQUEST_URI} !^/locales/
RewriteCond %{REQUEST_URI} !^/locales.json
RewriteCond %{REQUEST_URI} !^/admin
RewriteCond %{REQUEST_URI} !^/p/
RewriteCond %{REQUEST_URI} !^/static/
RewriteCond %{REQUEST_URI} !^/pluginfw/
RewriteCond %{REQUEST_URI} !^/javascripts/
RewriteCond %{REQUEST_URI} !^/socket.io/
RewriteCond %{REQUEST_URI} !^/ep/
RewriteCond %{REQUEST_URI} !^/minified/
RewriteCond %{REQUEST_URI} !^/api/
RewriteCond %{REQUEST_URI} !^/ro/
RewriteCond %{REQUEST_URI} !^/error/
RewriteCond %{REQUEST_URI} !^/jserror
RewriteCond %{REQUEST_URI} !^/redirect
RewriteCond %{REQUEST_URI} !/favicon.ico
RewriteCond %{REQUEST_URI} !/robots.txt
RewriteRule ^/+(.+)$ https://etherpad.example.org/p/$1 [L]
In case you are using plugins, don't forget to add to this list the RewriteCond needed for each one that use new link access. Some examples (not an exhaustive list. Check your apache logs in case of problems with a plugin):
- ep_list_pads plugin:
RewriteCond %{REQUEST_URI} !^/list/
RewriteCond %{REQUEST_URI} !^/public/
- ep_historicalsearch plugin:
RewriteCond %{REQUEST_URI} !^/historicalSearch
- ep_fileupload plugin:
RewriteCond %{REQUEST_URI} !^/fileUpload/
RewriteCond %{REQUEST_URI} !^/up/
During the setup phase, HAProxy can work in HTTP mode, processing layer 7 information. It detects automatically the Connection: Upgrade exchange and is ready to switch to tunnel mode if the upgrade negotiation succeeds. During this phase, there are 3 timeouts involved:
- timeout client: client inactivity
- timeout connect: allowed TCP connection establishment time
- timeout server: allowed time to the server to process the request
If everything goes well, the websocket is established, then HAProxy fails over to tunnel mode, no data is analyzed anymore (and anyway, websocket does not speak HTTP). There is a single timeout involved:
- timeout tunnel: take precedence over client and server timeout
timeout connect is not used since the TCP connection is already established 🙂
Sample config with redirect HTTP (:80) to HTTPS (:443): /etc/haproxy/haproxy.cfg
frontend http
mode http
bind *:80
bind *:443 ssl crt /path/to/your/cert.pem no-sslv3 alpn h2,http/1.1
option http-keep-alive
compression algo gzip
http-request redirect scheme https code 301 unless { ssl_fc }
acl PAD_ACL hdr(host) -i pad.exemple.net
use_backend PAD if PAD_ACL
backend PAD
option httplog
option http-server-close
option dontlognull
option redispatch
option contstats
retries 3
backlog 10000
timeout client 25s
timeout connect 5s
timeout server 25s
timeout tunnel 3600s
timeout http-keep-alive 1s
timeout http-request 15s
timeout queue 30s
timeout tarpit 60s
option forwardfor
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server PAD SERVER_IP:9001 ckeck
/etc/apache2/webapps/org.etherpad.etherpad-lite.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- See man pages for webapp.plist(5) and webappctl(8) for information about this example webapp.plist -->
<plist version="1.0">
<dict>
<key>includeFiles</key>
<array> <!-- Include files are activated in virtual host when webapp is started -->
<string>/etc/apache2/httpd_etherpad.conf</string>
</array>
<key>launchKeys</key>
<array> <!-- Launchd plists in /System/Library/LaunchDaemons are loaded when webapp is started -->
<!-- <string>com.example.mywebapp</string> -->
</array>
<key>name</key>
<string>org.etherpad.etherpad-lite</string>
<key>proxies</key> <!-- ProxyPass/ProxyPassReverse directives are activated when webapp is started -->
<dict>
<key>/etherpad</key> <!-- Sets up a reverse proxy -->
<dict>
<key>path</key>
<string>/etherpad</string>
<key>urls</key> <!-- URLs comprise a proxy_balancer group -->
<array>
<string>http://localhost:9001</string>
</array>
</dict>
</dict>
<key>requiredModuleNames</key>
<array> <!-- Apache plugin modules are enabled when webapp is started -->
<string>proxy_module</string>
</array>
<key>requiredWebAppNames</key>
<array> <!-- Required web apps are started when this webapp is started -->
<!-- <string>com.example.webapp.myotherwebapp</string> -->
</array>
<key>sslPolicy</key> <!-- Determines webapp SSL behavior -->
<integer>0</integer> <!-- 0: default, UseSSLWhenEnabled -->
<!-- 1: UseSSLAlways -->
<!-- 2: UseSSLOnlyWhenCertificateIsTrustable -->
<!-- 3: UseSSLNever -->
<!-- 4: UseSSLAndNonSSL -->
</dict>
</plist>
To restrict access only to a specific usergroup save the next listing to /etc/apache2/httpd_etherpad.conf
<Location "/etherpad">
AuthType Digest
AuthName "Access Etherpad-Lite"
Require group etherpad_users
</Location>
If you don't want to restrict access to specific group delete
<string>/etc/apache2/httpd_etherpad.conf</string>
from /etc/apache2/webapps/org.etherpad.etherpad-lite.plist
$HTTP["host"] =~ "^example.com$" {
proxy.server = ( "" => ( (
"host" => "127.0.0.1",
"port" => 9001
) )
)
}
If you use more than one server behind lighty (you can add more server - look at the lighty docs linked above) you have to use the hash balancer. So for any request the same server will be used. But remember: If you restart lighty this probably does not work anymore. (TODO: add synchronization in etherpad-lite)
Additional information: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxy
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name pad.example.com;
access_log /var/log/nginx/eplite.access.log;
error_log /var/log/nginx/eplite.error.log;
ssl on;
ssl_certificate /etc/nginx/ssl/eplite.crt;
ssl_certificate_key /etc/nginx/ssl/eplite.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 \
EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 \
EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
location / {
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass these headers etc too.
proxy_set_header X-Real-IP $remote_addr; # https://nginx.org/en/docs/http/ngx_http_proxy_module.html
proxy_set_header X-Forwarded-For $remote_addr; # EP logs to show the actual remote IP
proxy_set_header X-Forwarded-Proto $scheme; # for EP to set secure cookie flag when https is used
proxy_http_version 1.1; # recommended with keepalive connections
# WebSocket proxying - from https://nginx.org/en/docs/http/websocket.html
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
# we're in the http context here
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# enforce HTTPS
server {
listen 80;
listen [::]:80;
server_name pad.example.com;
return 301 https://$host$request_uri;
}
In newer versions of nginx you can use:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name pad.example.com;
access_log /var/log/nginx/eplite.access.log;
error_log /var/log/nginx/eplite.error.log;
ssl_certificate /etc/nginx/ssl/eplite.crt;
ssl_certificate_key /etc/nginx/ssl/eplite.key;
location / { ... }
}
...
server {
listen 80;
listen [::]:80;
location /pad {
rewrite /pad/(.*) /$1 break;
rewrite ^/pad$ /pad/ permanent;
proxy_redirect / /pad/;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
location /pad/socket.io {
rewrite /pad/socket.io/(.*) /socket.io/$1 break;
proxy_redirect / /pad/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Note you might want to pass more headers etc too. See above configs.
}
location /pad/admin {
rewrite /pad/admin/(.*) /admin/$1 break;
proxy_redirect / /pad/;
proxy_set_header X-Proxy-Path /pad;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
location /pad/admin-auth {
rewrite /pad/admin-auth/(.*) /admin-auth/$1 break;
proxy_redirect / /pad/;
proxy_set_header X-Proxy-Path /pad;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
location /pad/static {
rewrite /pad/static/(.*) /static/$1 break;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
}
server {
...
location / {
rewrite ^/$ / break;
rewrite ^/locales/(.*) /locales/$1 break;
rewrite ^/locales.json /locales.json break;
rewrite ^/admin(.*) /admin$1 break;
rewrite ^/p/(.*) /p/$1 break;
rewrite ^/static/(.*) /static/$1 break;
rewrite ^/pluginfw/(.*) /pluginfw/$1 break;
rewrite ^/javascripts/(.*) /javascripts/$1 break;
rewrite ^/socket.io/(.*) /socket.io/$1 break;
rewrite ^/ep/(.*) /ep/$1 break;
rewrite ^/minified/(.*) /minified/$1 break;
rewrite ^/api/(.*) /api/$1 break;
rewrite ^/ro/(.*) /ro/$1 break;
rewrite ^/error/(.*) /error/$1 break;
rewrite ^/jserror(.*) /jserror$1 break;
rewrite ^/redirect(.*) /redirect$1 break;
rewrite ^/(.*\.js) /$1 break;
rewrite /favicon.ico /favicon.ico break;
rewrite /robots.txt /robots.txt break;
rewrite /(.*) /p/$1;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
}
server {
...
# Allow normal files to pass through
location ~ ^/(locales/|locales.json|admin/|static/|pluginfw/|javascripts/|socket.io/|ep/|minified/|api/|ro/|error/|jserror/|favicon.ico|robots.txt|.*\.js) {
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
# Redirect to force /p/* URLs to the friendly version
location /p/ {
rewrite ^/p/(.*) /$1 redirect;
}
# Match the home page
location ~ ^/$ {
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
# Handle pad URLs here
location / {
proxy_redirect / /p/;
proxy_pass http://127.0.0.1:9001;
proxy_buffering off; # be careful, this line doesn't override any proxy_buffering on set in a conf.d/file.conf
proxy_set_header Host $host;
proxy_pass_header Server;
# Note you might want to pass more headers etc too. See above configs.
}
}
You need to download and install Ionics Isapi Rewrite Filter, when installing select the site you wish to use to reverse proxy. Browse to your websites root and create a file called IIRF.ini
Add this to the file:
RewriteBase OFF
ProxyPass ^/pad/(.*)$ http://etherpad.internal.example.org:9001/
ProxyPass ^/p/(.*)$ http://etherpad.internal.example.org:9001/p/$1
ProxyPass ^/static/(.*)$ http://etherpad.internal.example.org:9001/static/$1
ProxyPass ^/pluginfw/(.*)$ http://etherpad.internal.example.org:9001/pluginfw/$1
ProxyPass ^/javascripts/(.*)$ http://etherpad.internal.example.org:9001/javascripts/$1
ProxyPass ^/socket.io/(.*)$ http://etherpad.internal.example.org:9001/socket.io/$1
ProxyPass ^/minified/(.*)$ http://etherpad.internal.example.org:9001/minified/$1
ProxyPass ^/ep/(.*)$ http://etherpad.internal.example.org:9001/ep/$1
ProxyPass ^/api/(.*)$ http://etherpad.internal.example.org:9001/api/$1
ProxyPass ^/error/(.*)$ http://etherpad.internal.example.org:9001/error/$1
ProxyPass ^/jserror/(.*)$ http://etherpad.internal.example.org:9001/jserror/$1
ProxyPass ^/redirect(.*)$ http://etherpad.internal.example.org:9001/redirect$1
ProxyPassReverse / http://etherpad.internal.example.org:9001/
Save and close
Stop and Start the IIS site.
You need to install the Application Request Routing module to IIS first, using the Web platform installer. This is fairly straightforward to do: Open the Webplattform-Installer, e.g. by clicking your server in the server management console under Roles / Webserver (IIS) / Internet Information Services (I got this in German, so don't get confused when the terms are a little different on your installation - I'm trying to translate this back to English; if you follow these instructions on an English installation it would be nice if you could correct any wrong terms). It's in the Management section (probably the lowest entry in the list).
Here you will find "Application Request Routing" (currently 2.5). Click "Add" and then "Install". Wait.
Once the installation is complete, you'll find "Serverfarms" below "Sites" in your server. Add one with the name of your EtherPad Lite installation. Make sure to enter the correct ports under "Extended Settings" (I'm using 9001 for both httpPort and httpsPort).
Now be careful to not have the console create any default routing settings (that might route any request to any of the sites hosted on your machine to EtherPad Lite). Double click your newly created server farm, then double click "Routing Rules". Under "Advanced Routing", click "URL Rewrite..."
Make sure there are no rules (if any were created, delete them). Create a new empty rule.
You can almost follow the instructions from here:
BUT: If you have multiple sites hosted on that IIS, make sure to add conditions to check against the hostname. Use {HTTP_HOST} as "entry", "matches pattern" and your full domain for EtherPad Lite as pattern.
Finally, in action, I used "forward to server farm" with schema "http://", Serverfarm "the one I created before" and Path "/{R:0}". That's it.
For me, this worked both with HTTP and HTTPS.
The important part is the TimeOut value, as otherwise Pound will fail with 500 - Internal Server Error.
ListenHTTP
Address 0.0.0.0
Port 80
Service
HeadRequire "Host: .*example.com.*"
BackEnd
Address 127.0.0.1
Port 9001
TimeOut 300
End
End
End
backend etherpad {
.host = "localhost";
.port = "9001";
.probe = {
.url = "/";
.interval = 5s;
.timeout = 1s;
.window = 5;
.threshold = 3;
}
}
sub vcl_recv{
set req.backend = etherpad;
return (pipe);
}
example.com {
# Needed for some reason
redir /pad /pad/
}
example.com/etherpad/ {
proxy / localhost:9001
}
example.com/etherpad/socket.io {
proxy / localhost:9001/socket.io {
websocket
transparent
}
}
example.com/static {
proxy / localhost:9001/static
}
With Labels
traefik.docker.network yourfrontendnetwork # Required if connected to multiple Networks
traefik.enable true
traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto https # Required if eg Authelia is used
traefik.http.routers.etherpad.entrypoints websecure
traefik.http.routers.etherpad.middlewares sslheader
traefik.http.routers.etherpad.rule Host(`pad.yourdomain`)
traefik.http.routers.etherpad.service etherpad
traefik.http.routers.etherpad.tls.certresolver yourcertresolver
traefik.http.services.etherpad.loadbalancer 9001
traefik.http.services.etherpad.loadbalancer.passhostheader true
- Docs
- Translating
- HTTP API
- Plugin framework (API hooks)
- Plugins (available)
- Plugins (list)
- Plugins (wishlist)
- Etherpad URIs / URLs to specific resources IE export
- Etherpad Full data export
- Introduction to the source
- Release Procedure
- Etherpad Developer guidelines
- Project to-do list
- Changeset Library documentation
- Alternative Etherpad-Clients
- Contribution guidelines
- Installing Etherpad
- Deploying Etherpad as a service
- Deploying Etherpad on CloudFoundry
- Deploying Etherpad on Heroku
- Running Etherpad on Phusion Passenger
- Putting Etherpad behind a reverse Proxy (HTTPS/SSL)
- How to setup Etherpad on Ubuntu 12.04 using Ansible
- Migrating from old Etherpad to Etherpad
- Using Etherpad with MySQL
- Customizing the Etherpad web interface
- Enable import/export functionality with AbiWord
- Getting a list of all pads
- Providing encrypted web access to Etherpad using SSL certificates
- Optimizing Etherpad performance including faster page loads
- Getting to know the tools and scripts in the Etherpad /bin/ folder
- Embedding a pad using the jQuery plugin
- Using Embed Parameters
- Integrating Etherpad in a third party app (Drupal, MediaWiki, WordPress, Atlassian, PmWiki)
- HTTP API client libraries