All Products
Search
Document Center

Object Storage Service:How to force download of an OSS file when accessing it through a custom domain name?

Last Updated:Mar 20, 2026

By default, when a browser that supports inline preview opens an OSS object through a custom domain, OSS renders the object inline rather than downloading it. To force the browser to download the object as an attachment instead, set the Content-Disposition response header to attachment. OSS supports two methods, each with a different scope of effect.

Choose a method

MethodScopeWhen to use
Add response-content-disposition=attachment to a signed URLPer-request only. Only the specific signed URL triggers a download.Temporary or one-off download links
Set Content-Disposition: attachment in object metadataPermanent. Every browser access to the object triggers a download.Objects that should always be downloaded, regardless of how they are accessed

Use a signed URL to force a download

When a browser accesses a signed URL with response-content-disposition=attachment, the browser downloads the object as an attachment. Other accesses to the same object are not affected.

Use OSS SDK for Java

Prerequisites

Before you begin, make sure that:

  • The custom domain is mapped to the bucket (CNAME record configured)

  • The OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are set with valid credentials

The following example uses GeneratePresignedUrlRequest to generate a signed URL that includes response-content-disposition=attachment.

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import java.net.URL;
import java.util.*;
import java.util.Date;

public class Demo {
    public static void main(String[] args) throws Throwable {
        // Custom domain mapped to the bucket
        String endpoint = "https://example.com";
        // Load credentials from environment variables
        EnvironmentVariableCredentialsProvider credentialsProvider =
            CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
        // Required: map the custom domain (CNAME) to the bucket
        conf.setSupportCname(true);

        String bucketName = "examplebucket";
        String objectName = "exampleobject.txt";

        // Call shutdown() to release resources when the client is no longer in use
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider, conf);

        try {
            GeneratePresignedUrlRequest request =
                new GeneratePresignedUrlRequest(bucketName, objectName);
            request.setMethod(HttpMethod.GET);
            // Set URL expiration to 1 hour
            request.setExpiration(new Date(new Date().getTime() + 3600 * 1000L));

            // Add response-content-disposition=attachment to force download
            Map<String, String> queryParam = new HashMap<>();
            queryParam.put("response-content-disposition", "attachment");
            request.setQueryParameter(queryParam);

            URL url = ossClient.generatePresignedUrl(request);
            System.out.println(url);
        } catch (OSSException oe) {
            System.out.println("Error Message: " + oe.getErrorMessage());
            System.out.println("Error Code:    " + oe.getErrorCode());
            System.out.println("Request ID:    " + oe.getRequestId());
            System.out.println("Host ID:       " + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Error Message: " + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

When a user opens the generated URL in a browser, the browser presents a Save As dialog instead of rendering the object inline.

Set Content-Disposition in object metadata to force downloads permanently

Setting Content-Disposition: attachment in an object's metadata makes every browser access to that object trigger a download, regardless of the URL used. Configure this either through the OSS console or programmatically using the SDK.

Use the OSS console

  1. Log on to the OSS console.

  2. In the left-side navigation pane, click Buckets. On the Buckets page, find and click the desired bucket.

  3. In the left-side navigation tree, choose Object Management > Objects.

  4. Locate the object, then choose ![更多](https://help-static-aliyun-doc.aliyuncs.com/assets/img/en-US/3753398761/p539543.png) > Set Object Metadata.

  5. In the Set Object Metadata panel, set Content-Disposition to attachment, leave other settings unchanged, and click OK.

  6. Access the object using the custom domain URL:

    • Public-read or public-read-write ACL: Open http://example.com/example.jpg directly in a browser.

    • Private ACL: Generate a signed URL (for example, http://example.com/example.jpg?SignatureInfo) and open it in a browser. For details, see Use the custom domain name to access the bucket.

Use OSS SDK for Java

The following example uploads an object with Content-Disposition: attachment set in its metadata. After the upload, the object always triggers a download when accessed from a browser.

Prerequisites

Before you run the sample code, make sure that:

  • The OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are set with valid credentials

  • The custom domain is mapped to the bucket (CNAME record configured)

Sample code

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.ObjectMetadata;
import java.io.ByteArrayInputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;

public class Test {
    public static void main(String[] args) throws Exception {
        // Custom domain mapped to the bucket
        String endpoint = "https://example.com";
        // Load credentials from environment variables
        EnvironmentVariableCredentialsProvider credentialsProvider =
            CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
        // Required: map the custom domain (CNAME) to the bucket
        conf.setSupportCname(true);

        String bucketName = "examplebucket";
        String objectName = "testfolder/exampleobject.txt";
        String content = "Hello OSS";

        // Call shutdown() to release resources when the client is no longer in use
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider, conf);

        try {
            ObjectMetadata meta = new ObjectMetadata();

            // Enable MD5 verification: OSS compares the computed MD5 with this value
            // and returns an error if they differ
            String md5 = BinaryUtil.toBase64String(BinaryUtil.calculateMd5(content.getBytes()));
            meta.setContentMD5(md5);

            // Set content type. If omitted, OSS infers it from the file extension.
            // If no extension is present, defaults to application/octet-stream.
            meta.setContentType("text/plain");

            // Force download: set Content-Disposition to attachment.
            // URL-encode filenames that contain non-ASCII characters.
            meta.setContentDisposition("attachment" + URLEncoder.encode("UTF-8") + ";" + URLEncoder.encode("UTF-8"));

            // Upload the object with metadata
            ossClient.putObject(
                bucketName,
                objectName,
                new ByteArrayInputStream(content.getBytes()),
                meta
            );

            // Verify: generate a signed URL and open it in a browser to confirm download behavior
            Date expiration = new Date(new Date().getTime() + 3600 * 1000);
            GeneratePresignedUrlRequest signRequest =
                new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.GET);
            signRequest.setExpiration(expiration);
            URL signedUrl = ossClient.generatePresignedUrl(signRequest);
            System.out.println(signedUrl);

        } catch (OSSException oe) {
            System.out.println("Error Message: " + oe.getErrorMessage());
            System.out.println("Error Code:    " + oe.getErrorCode());
            System.out.println("Request ID:    " + oe.getRequestId());
            System.out.println("Host ID:       " + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Error Message: " + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

Open the signed URL printed to the console in a browser. The browser should present a Save As dialog, confirming that the metadata is applied correctly.

What's next

References