🛡️ Discord Proxy
Client network requests made by your Discord Activity are "sandboxed" through Discord's Proxy.
That means you cannot directly make network requests to external URLs from it for security reasons. You may have seen this if you've tried using iframes. There are also limitations to what network protocols you can use.
Content Security Policy (CSP)
Ever tried to load a URL from an external resource and got a blocked:csp
error? That's the Discord Proxy doing its job.
Content Security Policy (CSP) is a security feature that helps prevent cross-site scripting (XSS) attacks. It restricts what can load, such as scripts, styles, and images. Discord trusts only you and itself. It will throw CSP errors at everyone else.
It may be a pain, but it's for your own good.
Proxy Rules
The Discord Proxy has a few rules you need to follow:
- No external URLs: You can't load external resources directly.
- Protocols and Ports: Only HTTPS, derivatives, and some protocols are allowed.
- Prefix /.proxy for relative paths: Requests made within the activity should be prefixed with
/.proxy
. - Respect CSP directives: They are unique for each activity, like
https://<CLIENT_ID>.discordsays.com/.proxy/
.
You can automate your CSP compliance by using @robojs/patch to patch URLs in your code with zero effort. All Robo.js activity projects come with this plugin pre-installed by default.
Fetching External Resources
You can friend the Discord Proxy by using URL Mappings, patching URLs, or proxying requests.
URL Mapping
Mapping URLs is a way to tell the Discord Proxy to allow certain external resources. You can map a URL to a specific path in the Discord Developer Portal. This is the recommended way to allow external resources you need in your Discord Activity.
Let's say you need to load something from GitHub. You can map it to a path like /github
and /assets
.
/github
https://github.com
/assets
https://raw.githubusercontent.com/Wave-Play/robo.js/main
This configuration maps URLs you can use in your activity like so:
/github/Wave-Play/robo.js
https://github.com/Wave-Play/robo.js
/assets/README.md
https://raw.githubusercontent.com/Wave-Play/robo.js/main/README.md
And voila, you can now load those URLs in your Discord Activity using the mapped paths.
<iframe src="/assets/README.md"></iframe>
fetch('/assets/docs/static/img/logo.png')
You can reference it as a path or construct a full URL.
GitHub is not a CDN. Connect your host to a CDN like Cloudflare or Bunny.net for your assets.
URL Patching
Mapping is great if you have control over the paths used, but what if you don't? You know, like when using third-party libraries with hardcoded URLs. You can use the Embedded App SDK for that!
import { patchUrlMappings } from '@discord/embedded-app-sdk'
patchUrlMappings([{ prefix: '/foo', target: 'foo.com' }])
Alternatively, you can use a post-install utility like patch-package or fork the library to use mapped URLs.
Proxying Requests
As a last resort, you can proxy requests through your Web Server. This is useful when you need to make requests to a specific port or handle requests that the Discord Proxy can't, but it's not always the best solution.
Creating your own proxy is easy. Just create a file in /src/api
called proxy.js
with the following code:
export default async (request) => {
return fetch(request.query.url)
}
Modify as you see fit to validate requests. You can use it like so inside your Activity Client:
<Player url={'/api/proxy?url=' + ExternalUrl} />
This code basically has your Web Server to fetch the data and send it back to your Activity Client.
While this method is effective for bypassing CSP restrictions, it does introduce additional latency because your server has to fetch the resource before serving it back to the activity. This may impact the performance of your activity, especially for large files or high traffic, so make sure your Hosting Service can handle it.
Be aware of potential security risks when using a proxy server, such as URL Injections and SSRF (Server-Side Request Forgery) attacks. Always use Discord's URL Mapping whenever possible.
Security Considerations
The Discord Proxy hides the user's IP address and blocks URLs from known malicious endpoints. This ensures the safety of the user's data and privacy. However, it also means that you need to be careful when handling external resources.
Don't trust info coming from the Embedded App SDK as truth. There could be an impostor among us. Call the Discord API directly from your app's Web Server with the OAuth2 token you received during Authentication if you need information that's not sus.
Always sanitize user inputs such as usernames and channel names. You never know what might try to sneak in.
Network Limitations
Want to use WebRTC or WebTransport? Sorry, you can't. For now, at least.
The Discord Proxy does not support the following:
You'll be fine if you stick to WebSockets or HTTPS, as well as some protocols built on them like DASH or HLS for streaming.
Need to make a request to a specific port? Have your Web Server handle it by proxying the request. That won't always work on iframes, though. You may need to virtualize the source and livestream it from your server but that has its own limitations.