Resolving Shopify App Proxy 404 Errors with Remix

I was trying to do POST request to the api.referral.jsx file and it shows 404 error

api.referral.jsx

import { json } from “@remix-run/node”;> import { cors } from “remix-utils/cors”;> import prisma from “../db.server”;> import { authenticate } from “../shopify.server”;> export async function action({ request }) {> const { session } = authenticate.public.appProxy(request);> console.log(session);> const urlParams = new URL(request.url).searchParams;> const ref = urlParams.get(“ref”);> const { method } = request;> switch (method) {> case “POST”:> try {> if (!ref) {> return cors(> request,> json({ error: “Missing referral ID” }, { status: 400 }),> );> }> const affiliateLink = await prisma.affiliateLink.findFirst({> where: { uniqueId: ref },> include: { affiliate: true },> });> if (!affiliateLink) {> return cors(> request,> json({ error: “Invalid referral ID” }, { status: 404 }),> );> }> const currentDate = new Date();> const startOfDay = new Date(currentDate);> startOfDay.setHours(0, 0, 0, 0);> const endOfDay = new Date(currentDate);> endOfDay.setHours(23, 59, 59, 999);> let performance = await prisma.affiliatePerformance.findFirst({> where: {> affiliateId: affiliateLink.affiliateId,> startDate: { lte: currentDate },> endDate: { gte: currentDate },> },> });> if (performance) {> performance = await prisma.affiliatePerformance.update({> where: { id: performance.id },> data: {> totalClicks: { increment: 1 },> updatedAt: currentDate,> },> });> } else {> performance = await prisma.affiliatePerformance.create({> data: {> affiliateId: affiliateLink.affiliateId,> totalClicks: 1,> startDate: startOfDay,> endDate: endOfDay,> },> });> }> return cors(> request,> json({> success: true,> message: “Click recorded successfully”,> }),> );> } catch (error) {> console.error(“Error processing referral click:”, error);> return cors(> request,> json(> {> error: “Failed to process referral click”,> details: error.message,> },> { status: 500 },> ),> );> }> default:> return cors(> request,> json({ error: “Method not allowed” }, { status: 405 }),> );> }> }
Liquid File

document.addEventListener(‘DOMContentLoaded’, async () => {> const urlParams = new URLSearchParams(window.location.search);> const ref = urlParams.get(‘ref’);> > if (!ref) return;> > try {> const response = await fetch(/apps/affiliate/?ref=${ref}, {> method: ‘POST’,> headers: { ‘Content-Type’: ‘application/json’ },> });> > if (!response.ok) {> throw new Error(Server responded with status ${response.status});> }> > const result = await response.json();> console.log(‘Referral logged successfully:’, result);> } catch (err) {> console.error(‘Error sending referral:’, err);> }> });> > > {% schema %}> {> “name”: “Earnetra Referral Block”,> “target”: “section”,> “settings”: > }> > {% endschema %}> >

Hello @harishraghav Thanks for sharing the full setup. The 404 error you’re seeing when trying to do a POST request through the Shopify App Proxy is most likely caused by Shopify App Proxy limitations, particularly with HTTP methods.

Shopify App Proxy Limitation
Shopify App Proxy only supports GET requests by default. Any other method (like POST) will return a 404.

Solutions
Option 1: Switch to GET
If you’re only tracking a referral click and not modifying sensitive data, you could use a GET request instead of POST. Update your JS:

const response = await fetch(`/apps/affiliate?ref=${ref}`, {
  method: 'GET',
});

And adjust your api.referral.jsx to handle GET instead of POST.

Option 2: Use a Proxy Route in Your App’s Backend
Instead of using Shopify’s app proxy, create a public route on your Remix app (e.g. /track-referral) and send the request directly to that from your Liquid code:

const response = await fetch('https://yourappdomain.com/track-referral', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ ref }),
});

That way, you bypass Shopify’s app proxy limitations altogether.

Option 3: Workaround Using Hidden Form Submission
If you must use the App Proxy and can’t expose a public URL, you could fake a POST request via form submission:


Then, handle it in your Remix loader instead of action.

Let me know which direction you’d like to go (use GET, create public endpoint, or workaround with form), and I can help update the code accordingly.

Thank you :blush:

Hey harishraghav,
This looks like a solid setup, but the 404 issue when hitting your api.referral.jsx route via the app proxy could be due to how Shopify App Proxy maps requests.

In Shopify, requests to /apps/affiliate/ are routed through the app proxy, and that path needs to be explicitly handled in your Remix app under the app/routes/apps/affiliate.jsx (or similar) to match the proxy path. Right now, it looks like your route might not be correctly aligned with the proxy URL pattern, which is likely why it’s returning a 404.

A few tips to check:

  • Make sure you’ve defined the correct app proxy path in your Shopify Partner dashboard (e.g., /apps/affiliate) and that it’s hitting the right Remix route file.

  • In Remix, the route file should reflect the proxy path (like app/routes/apps/affiliate.jsx) and export the correct action handler.

  • Ensure the route is deployed and built properly sometimes route mismatches happen when filenames or directory structure is off.

If you’re also dealing with any NGINX or proxy level configuration in front of your app, make sure those aren’t rewriting paths in unexpected ways.

I work a lot with proxies and recently put together a resource that explains some common proxy issues and resolutions feel free to check it out here: yt It’s not Shopify-specific but might give some broader ideas on debugging proxy layers.

Hope this helps good luck with the build!