If you're building a Discord Activity, you may encounter issues with Content Security Policy (CSP) restrictions.
CSP is a security feature that helps prevent cross-site scripting attacks by restricting the resources a web page can load. However, it can sometimes interfere with loading external resources like images, fonts, or scripts in your activity.
The recommended fix is to use Discord's URL Mapping feature to rewrite URLs and bypass CSP restrictions. However, this method has limitations and may not work in all cases.
Proxies to the Rescue
An alternative solution is to use a proxy server to fetch external resources and serve them from your server. This way, you can bypass CSP restrictions and load resources without any issues.
The way it works is simple:
- Your activity makes a request to your server for an external resource.
- Your server fetches the resource and streams it back as a response.
- Your activity receives the resource from your server, bypassing CSP restrictions.
This method allows you to load any content from any source without having to map each domain individually in Discord's URL Mapping. It may sound complex, but it's literally one line of code if you created your Discord Activity with Robo.js.
Create a Discord Activity Project
Create a Discord Activity with Robo.js if you don't have one already:
npx create-robo my-activity -k activity
We'll be using React for this example, but you can apply the same principles to any frontend framework or vanilla HTML and CSS.
We highly recommend using create-robo to create a Discord Activity project. Robo.js providers a lot of features and tools to help you build your activity faster and more efficiently, such as Multiplayer Support, Easy Hosting, Streamlined Tunnels, Built-in Database, Plugin System, and so much more!
Creating a Discord Activity in seconds with Robo.js
WavePlay Staff for WavePlay ・ Apr 9
Making a Music Player
Let's say you want to add an audio player to your activity that plays music from an external source. You might run into CSP issues when trying to load the audio file due to restrictions on loading external resources.
Let's create a simple music player using the <audio>
element in React by adding a new file named Player.tsx
inside the /src/app
folder:
import { useState, useRef } from 'react'
export const Player = (props) => {
const { url } = props
const audioRef = useRef(null)
const [isPlaying, setIsPlaying] = useState(false)
const togglePlayPause = () => {
const audio = audioRef.current
if (audio && isPlaying) {
audio.pause()
} else if (audio && !isPlaying) {
audio.play()
}
setIsPlaying(!isPlaying)
}
return (
<>
<audio ref={audioRef} src={url} preload="auto" />
<button onClick={togglePlayPause}>{isPlaying ? 'Pause' : 'Play'}</button>
</>
)
}
Then update your Activity.tsx
file to use it:
import { Player } from './Player'
const ExternalUrl = 'https://media.waveplay.com/t/ckwldfuiq6608re6x8dzc5tyt.mp3'
export const Activity = () => {
return (
<div>
<img src="/rocket.png" className="logo" alt="Discord" />
<h1>Hello, World</h1>
<Player url={ExternalUrl} />
<p>
<small>
Powered by{' '}
<a className="robojs" href="https://roboplay.dev/docs">
Robo.js
</a>
</small>
</p>
</div>
)
}
In this example, we're loading an audio file from an external source and playing it using the <audio>
element. However, this also triggers a CSP violation because https://media.waveplay.com
is not whitelisted in Discord's Proxy.
Let's fix this by creating a proxy!
Create Your Own Proxy
Create a new file named proxy.js
inside the /src/api
folder:
export default async (request) => {
return fetch(request.query.url)
}
That's it!
The @robojs/server plugin used in this Robo.js project extends the standard web Request and Response interfaces which work seamlessly with Fetch API, which is why we can return the result directly.
Heads up! You'll need at least @robojs/server version
0.5.3
or newer.
Now, update your Activity.tsx
file to use the proxy:
<Player url={'/api/proxy?url=' + ExternalUrl} />
By adding the /api/proxy?url=
prefix to the audio URL, we're telling our server to fetch the audio file and serve it back to the activity without triggering CSP violations.
You can use this method to proxy any external resource in your activity, such as images, fonts, scripts, or other media files. For more complex scenarios, such as web pages, our upcoming @robojs/browser plugin will provide a more robust solution.
Limitations
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 and only resort to proxies when necessary.
Easy, Right?
By creating a simple proxy server, you can bypass CSP restrictions and load external resources in your Discord Activity without any issues. This method is easy and effective, allowing you to focus on building your activity without worrying about security restrictions. You can find the complete source code for this example available in TypeScript as a Robo.js Template.
➞ 🔗 Template: Music Player Proxy
Don't forget to join our Discord server to chat with other developers, ask questions, and share your projects. We're here to help you build amazing apps with Robo.js! 🚀
Our very own Robo, Sage, is there to answer any questions about Robo.js, Discord.js, and more!
Top comments (0)