New to KendoReactStart a free 30-day trial

Server Proxy
Premium

Updated on Sep 15, 2026

A server proxy is an intermediate endpoint that receives the file content from the client and streams it back to the browser as a downloadable attachment. Use a server proxy when:

  • Your application runs in an environment where browser security policies restrict direct client-side file creation.
  • The file size exceeds 1 MB, which can cause Unknown network error failures in direct-download scenarios.
  • You need to attach server-side context to the download request, such as a CSRF token or audit metadata.

To save files, the File Saver package:

  1. Uses the native client-side file-saving API available in modern browsers.
  2. Falls back to a server-side proxy method that receives the content and sends it back to the browser as an attachment to save.

Enabling Proxies

If the native client-side API is unavailable, saveAs falls back to POST-ing the file content to a server proxy. The proxy decodes the content and streams it back with the appropriate response headers. To enable this fallback, set the proxyURL option.

The proxy only supports data URIs. Passing a Blob to saveAs when a proxy is active is not supported and will result in an error.

The following example shows how to export a generated PDF invoice with a proxy fallback:

js
import { saveAs } from '@progress/kendo-file-saver';

// pdfDataURI is a base64-encoded data URI produced by a PDF generation library
saveAs(pdfDataURI, "invoice-2024-0042.pdf", {
  proxyURL: "/api/files/proxy",
});

Forcing Proxy Usage

Set the forceProxy to true to always route downloads through the server, regardless of browser capability. This is recommended for large exports or when your application policy requires all file downloads to pass through the server. Note that setting forceProxy: true without also providing a proxyURL throws an error.

Use the optional proxyData field to send additional form parameters alongside the file content—for example, a CSRF token required by your backend:

js
import { saveAs } from '@progress/kendo-file-saver';

// excelDataURI holds the base64-encoded XLSX content of a large sales report
saveAs(excelDataURI, "sales-report-q4-2024.xlsx", {
  proxyURL: "/api/files/proxy",
  forceProxy: true,
  proxyData: { _csrf: getCsrfToken() },
});

Proxy Request and Response

Set the proxyURL option to the URL of your server-side proxy endpoint. The proxy must be implemented by the developer and is responsible for decoding the file content and returning it to the browser with the appropriate response headers.

The proxy receives a POST request with the following parameters in the request body:

  • contentType—The MIME type of the file (for example, application/pdf or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet).
  • base64—The Base64-encoded file content.
  • fileName—The file name as requested by the caller.
  • Additional fields passed through the optional proxyData option.

The proxy request is submitted as an HTML form POST, so the response opens directly in the browser window.

Use the optional proxyTarget option to control the target window. By default, it is _self. Set it to _blank to open the download in a new tab, or to the name of an existing iframe to keep the main page unaffected:

js
saveAs(pdfDataURI, "invoice-2024-0042.pdf", {
  proxyURL: "/api/files/proxy",
  proxyTarget: "_blank",
});

The proxy must return the decoded file with a Content-Disposition: attachment response header. When you set the proxyTarget to _blank or to the name of an existing iframe, set the header to Content-Disposition: inline; filename="<fileName.ext>" so the browser renders the response in the target frame instead of triggering a second download dialog.

ASP.NET Web API

C#
[RoutePrefix("api/files")]
public class FileProxyController : ApiController
{
    [HttpPost]
    [Route("proxy")]
    public async Task<HttpResponseMessage> Proxy()
    {
        var form = await Request.Content.ReadAsFormDataAsync();
        string contentType = form["contentType"];
        string base64 = form["base64"];
        string fileName = form["fileName"];

        var data = Convert.FromBase64String(base64);

        var result = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StreamContent(new MemoryStream(data))
        };

        result.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = fileName
        };

        return result;
    }
}

ASP.NET Core MVC

C#
[ApiController]
[Route("api/files")]
public class FileProxyController : ControllerBase
{
    [HttpPost("proxy")]
    public IActionResult Proxy(
        [FromForm] string contentType,
        [FromForm] string base64,
        [FromForm] string fileName)
    {
        var fileContents = Convert.FromBase64String(base64);
        return File(fileContents, contentType, fileName);
    }
}

ASP.NET WebForms

C#
public partial class FileProxy : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string contentType = Request.Form["contentType"];
        string base64 = Request.Form["base64"];
        string fileName = Request.Form["fileName"];

        byte[] fileContents = Convert.FromBase64String(base64);

        var contentDisposition = new System.Net.Mime.ContentDisposition
        {
            FileName = fileName,
            Inline = false
        };

        Response.ContentType = contentType;
        Response.AppendHeader("Content-Disposition", contentDisposition.ToString());
        Response.BinaryWrite(fileContents);
        Response.End();
    }
}

PHP

php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $fileName = $_POST['fileName'];
    $contentType = $_POST['contentType'];
    $base64 = $_POST['base64'];

    $data = base64_decode($base64);

    header('Content-Type: ' . $contentType);
    header('Content-Length: ' . strlen($data));
    header('Content-Disposition: attachment; filename="' . $fileName . '"');

    echo $data;
}
?>

Java (Spring MVC)

java
@RestController
@RequestMapping("/api/files")
public class FileProxyController {

    @PostMapping("/proxy")
    public void proxy(
            @RequestParam String fileName,
            @RequestParam String base64,
            @RequestParam String contentType,
            HttpServletResponse response) throws IOException {

        byte[] data = DatatypeConverter.parseBase64Binary(base64);

        response.setContentType(contentType);
        response.setContentLength(data.length);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        response.getOutputStream().write(data);
        response.flushBuffer();
    }
}