GeoServer cog-s3 extension calculated signature does not match request

Hi all,

currently I am trying to access GeoTiff data from an S3 compatible service through the cog-s3 community extension.

For the S3 service, which requires authentication, requests are not properly signed and thus fail.

Has someone got this to work with a recent version of GeoServer (2.28 or above)?

A Python script that generates successful authenticated requests is attached below.

Comparing the logs of this script and GeoServer shows some differences in the request signing process.

Hopefully someone more knowledgeable with AWSv4 signing can tell why the cog-s3 request fails?

GeoServer 3.1

On GeoServer 3.1 nightly, setting up the GeoTiff datastore fails with:

java.util.concurrent.ExecutionException: software.amazon.awssdk.services.s3.model.S3Exception: The request signature we calculated does not match the signature you provided (Service: S3, Status Code: 403, Request ID: 43f6e86a-8e54-9dff-8eb8-142aa1c23811) (SDK Attempt Count: 1)

The server runs with:

docker run -it -p 8080:8080 \
--mount type=bind,src=./tmpgs31/,target=/opt/geoserver_data \
-e IIO_S3_AWS_ENDPOINT=https://dataspace.copernicus.eu/ \
-e IIO_S3_AWS_REGION=US-EAST-1 \
-e INSTALL_EXTENSIONS=true \
-e COMMUNITY_EXTENSIONS="cog-s3" \
docker.osgeo.org/geoserver:3.1.x

The GeoTIFF datastore settings are:

URL: cog://s3://eodata/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif
Cloud Optimized GeoTiff: Enabled
Range Reader: S3
User name: MYACCESSKEY
Password: MYSECRETKEY

Note I have to omit eodata prefix with the endpoint, otherwise the host becomes eodata.eodata.dataspace.copernicus.eu which won’t work.
Apparently, the cog-s3 extension strips the bucket name from the URL and prepends it to the endpoint.

GeoServer 2.28.5

With GeoServer 2.28.5, it fails with:

java.util.concurrent.ExecutionException: software.amazon.awssdk.services.s3.model.S3Exception: The request signature we calculated does not match the signature you provided (Service: S3, Status Code: 403, Request ID: e191fae5-965c-9495-959d-6eca4bf0add7)

The full error log is attached below.

For 2.28.5 I use a custom build with cog-s3 plugin installed and environment variables and configuration as above.

s3cmd Test

To confirm access is working, I use s3cmd:

s3cmd -c ~/.s3cfg get s3://eodata/CLMS/landcover_landuse/dynamic_land_cover/lc_global_100m_yearly_v3/2019/01/01/PROBAV_LC100_global_v3.1.2_2019_cog/PROBAV_LC100_global_v3.1.2_2019-nrt_BuiltUp-CoverFraction-layer_EPSG-4326.tif

with the file ~/.s3cfg configured as described in Access to EO data via S3 – Documentation :

[default]
access_key = MYACCESSKEY
host_base = eodata.dataspace.copernicus.eu
host_bucket = eodata.dataspace.copernicus.eu
human_readable_sizes = False
secret_key = MYSECRETKEY
use_https = true
check_ssl_certificate = true

Working Example Script in Python

This script adapted from the CDSE example generates a successful request with the same credentials and URL, and a complete log of the request signing.

A full log of a successful request is attached.

There are two obvious differences: the cog-s3 extension uses more signed headers, and the payload is unsigned.

#!/usr/bin/env python3

import os
import boto3
boto3.set_stream_logger(name='botocore')
from tqdm import tqdm
import time
import logging
logging.getLogger('botocore').setLevel(logging.DEBUG)

# Configuration parameters
config = {
    "s3_endpoint_url": "https://eodata.dataspace.copernicus.eu",
}

def format_filename(filename, length=40):
    """
    Format a filename to a fixed length, truncating if necessary.
    """
    if len(filename) > length:
        return filename[:length - 3] + '...'
    else:
        return filename.ljust(length)

def download_file_s3(s3, bucket_name, s3_key, local_path, failed_downloads):
    """
    Download a file from S3 with a progress bar.
    Track failed downloads in a list.
    """
    try:
        file_size = s3.head_object(Bucket=bucket_name, Key=s3_key)['ContentLength']
        formatted_filename = format_filename(os.path.basename(local_path))
        with tqdm(total=file_size, unit='B', unit_scale=True, desc=formatted_filename, ncols=80, bar_format='{desc:.40}|{bar:20}| {percentage:3.0f}% {n_fmt}/{total_fmt}B') as pbar:
            def progress_callback(bytes_transferred):
                pbar.update(bytes_transferred)

            s3.download_file(bucket_name, s3_key, local_path, Callback=progress_callback)
    except Exception as e:
        print(f"Failed to download {s3_key}. Error: {e}")
        failed_downloads.append(s3_key)

def traverse_and_download_s3(s3_resource, bucket_name, base_s3_path, local_path, failed_downloads):
    """
    Traverse the S3 bucket and download all files under the specified prefix.
    """
    bucket = s3_resource.Bucket(bucket_name)
    files = bucket.objects.filter(Prefix=base_s3_path)

    for obj in files:
        s3_key = obj.key
        relative_path = os.path.relpath(s3_key, base_s3_path)
        local_path_file = os.path.join(local_path, relative_path)
        local_dir = os.path.dirname(local_path_file)
        os.makedirs(local_dir, exist_ok=True)
        download_file_s3(s3_resource.meta.client, bucket_name, s3_key, local_path_file, failed_downloads)

def main():

    bucket_name, base_s3_path = 'eodata', 'CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog'

    aws_access_key_id='MYACCESSKEY'
    aws_secret_access_key='MYSECRETKEY'

    time.sleep(5)  # Ensure the key pair is installed
    s3_resource = boto3.resource('s3',
                                 endpoint_url=config["s3_endpoint_url"],
                                 aws_access_key_id=aws_access_key_id,
                                 aws_secret_access_key=aws_secret_access_key)

    top_level_folder = 'CLMS'
    os.makedirs(top_level_folder, exist_ok=True)
    failed_downloads = []
    traverse_and_download_s3(s3_resource, bucket_name, base_s3_path, top_level_folder, failed_downloads)

    if not failed_downloads:
        print("Product download complete.")
    else:
        print("Product download incomplete:")
        for failed_file in failed_downloads:
            print(f"- {failed_file}")

if __name__ == "__main__":
    main()

Working Example Script Log

2026-10-12 22:50:06,630 botocore.endpoint [DEBUG] Making request for OperationModel(name=GetObject) with params: {'url_path': '/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif', 'query_string': {}, 'method': 'GET', 'headers': {'x-amz-checksum-mode': 'ENABLED', 'User-Agent': 'Boto3/1.43.112 md/Botocore#1.43.112 ua/2.1 os/linux#6.8.0-142-generic md/arch#x86_64 lang/python#3.11.13 md/pyimpl#CPython m/b,G,N,Z,e,D cfg/retry-mode#legacy Botocore/1.43.112 Resource'}, 'body': b'', 'auth_path': '/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif', 'url': 'https://eodata.dataspace.copernicus.eu/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif', 'context': {'client_region': 'us-east-1', 'client_config': <botocore.config.Config object at 0x7aaddccc1ad0>, 'has_streaming_input': False, 'auth_type': 'v4', 'unsigned_payload': None, 'auth_options': ['aws.auth#sigv4'], 's3_redirect': {'redirected': False, 'bucket': 'eodata', 'params': {'Bucket': 'eodata', 'Key': 'CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif', 'ChecksumMode': 'ENABLED'}}, 'input_params': {'Bucket': 'eodata', 'Key': 'CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif'}, 'signing': {'region': 'us-east-1', 'signing_name': 's3', 'disableDoubleEncoding': True}, 'endpoint_properties': {'authSchemes': [{'disableDoubleEncoding': True, 'name': 'sigv4', 'signingName': 's3', 'signingRegion': 'us-east-1'}]}, 'checksum': {'response_algorithms': ['crc32', 'sha1', 'sha256', 'sha512']}}}
2026-10-12 22:50:06,631 botocore.hooks [DEBUG] Event request-created.s3.GetObject: calling handler <function signal_not_transferring at 0x7aadddd200e0>
2026-10-12 22:50:06,631 botocore.hooks [DEBUG] Event request-created.s3.GetObject: calling handler <bound method RequestSigner.handler of <botocore.signers.RequestSigner object at 0x7aaddccc1a90>>
2026-10-12 22:50:06,632 botocore.hooks [DEBUG] Event choose-signer.s3.GetObject: calling handler <bound method ClientCreator._default_s3_presign_to_sigv2 of <botocore.client.ClientCreator object at 0x7aaddd071310>>
2026-10-12 22:50:06,632 botocore.hooks [DEBUG] Event choose-signer.s3.GetObject: calling handler <function set_operation_specific_signer at 0x7aaddd9427a0>
2026-10-12 22:50:06,632 botocore.hooks [DEBUG] Event before-sign.s3.GetObject: calling handler <function remove_arn_from_signing_path at 0x7aaddd9691c0>
2026-10-12 22:50:06,633 botocore.hooks [DEBUG] Event before-sign.s3.GetObject: calling handler <function _set_extra_headers_for_unsigned_request at 0x7aaddd9698a0>
2026-10-12 22:50:06,633 botocore.hooks [DEBUG] Event before-sign.s3.GetObject: calling handler <bound method S3ExpressIdentityResolver.resolve_s3express_identity of <botocore.utils.S3ExpressIdentityResolver object at 0x7aaddcceac50>>
2026-10-12 22:50:06,633 botocore.auth [DEBUG] Calculating signature using v4 auth.
2026-10-12 22:50:06,633 botocore.auth [DEBUG] CanonicalRequest:
GET
/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif

host:eodata.dataspace.copernicus.eu
x-amz-checksum-mode:ENABLED
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20261012T205006Z

host;x-amz-checksum-mode;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
2026-10-12 22:50:06,633 botocore.auth [DEBUG] StringToSign:
AWS4-HMAC-SHA256
20261012T205006Z
20261012/us-east-1/s3/aws4_request
3c09c66e8cbc3156482c80d755e32a1edf32478f45a01b0994d026b08a467730
2026-10-12 22:50:06,634 botocore.auth [DEBUG] Signature:
d56a00f3c24ca6f0f251d217dc0eff298b8239cdb2332d128a4f845c39738749
2026-10-12 22:50:06,634 botocore.hooks [DEBUG] Event request-created.s3.GetObject: calling handler <bound method UserAgentString.rebuild_and_replace_user_agent_handler of <botocore.useragent.UserAgentString object at 0x7aaddcceddd0>>
2026-10-12 22:50:06,634 botocore.hooks [DEBUG] Event request-created.s3.GetObject: calling handler <function signal_transferring at 0x7aadddc9f380>
2026-10-12 22:50:06,634 botocore.hooks [DEBUG] Event request-created.s3.GetObject: calling handler <function add_retry_headers at 0x7aaddd968fe0>
2026-10-12 22:50:06,635 botocore.endpoint [DEBUG] Sending http request: <AWSPreparedRequest stream_output=True, method=GET, url=https://eodata.dataspace.copernicus.eu/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif, headers={'x-amz-checksum-mode': b'ENABLED', 'User-Agent': b'Boto3/1.43.112 md/Botocore#1.43.112 ua/2.1 os/linux#6.8.0-142-generic md/arch#x86_64 lang/python#3.11.13 md/pyimpl#CPython m/b,G,N,Z,e,D cfg/retry-mode#legacy Botocore/1.43.112 Resource', 'X-Amz-Date': b'20261012T205006Z', 'X-Amz-Content-SHA256': b'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'Authorization': b'AWS4-HMAC-SHA256 Credential=MYACCESSKEY/20261012/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-checksum-mode;x-amz-content-sha256;x-amz-date, Signature=d56a00f3c24ca6f0f251d217dc0eff298b8239cdb2332d128a4f845c39738749', 'amz-sdk-invocation-id': b'344d6830-f734-45de-ade4-1393e074c9cf', 'amz-sdk-request': b'attempt=1'}>
2026-10-12 22:50:06,635 botocore.httpsession [DEBUG] Certificate path: /home/stefan/source/spotteron/service-geoserver/cdse-test/.venv/lib/python3.11/site-packages/certifi/cacert.pem
2026-10-12 22:50:06,747 botocore.httpchecksum [DEBUG] Skipping checksum validation. Response did not contain one of the following algorithms: ['crc32', 'sha1', 'sha256', 'sha512'].
2026-10-12 22:50:06,748 botocore.hooks [DEBUG] Event before-parse.s3.GetObject: calling handler <function _handle_200_error at 0x7aaddd969580>
2026-10-12 22:50:06,748 botocore.hooks [DEBUG] Event before-parse.s3.GetObject: calling handler <function handle_expires_header at 0x7aaddd9693a0>
2026-10-12 22:50:06,748 botocore.parsers [DEBUG] Response headers: {'accept-ranges': 'bytes', 'content-length': '5150452', 'content-type': 'binary/octet-stream', 'date': 'Mon, 12 Oct 2026 20:50:06 GMT', 'etag': '"c428c0097e6c89db14f39959595910ce"', 'last-modified': 'Fri, 06 Mar 2026 03:01:26 GMT', 'server': 'envoy', 'x-amz-meta-mtime': '1750063271', 'x-amz-request-id': 'tx00000ca5331cf0e8cb2db-006acd47fe-558416165-default', 'x-rgw-object-type': 'Normal', 'x-envoy-upstream-service-time': '65', 'x-ratelimit-limit': '2000, 2000;w=60', 'x-ratelimit-remaining': '1996', 'x-ratelimit-reset': '54'}
2026-10-12 22:50:06,748 botocore.parsers [DEBUG] Response body:
<botocore.response.StreamingBody object at 0x7aaddcbc55a0>
2026-10-12 22:50:06,749 botocore.hooks [DEBUG] Event needs-retry.s3.GetObject: calling handler <function _update_status_code at 0x7aaddd969760>
2026-10-12 22:50:06,750 botocore.hooks [DEBUG] Event needs-retry.s3.GetObject: calling handler <botocore.retryhandler.RetryHandler object at 0x7aaddccea5d0>
2026-10-12 22:50:06,750 botocore.retryhandler [DEBUG] No retry needed.
2026-10-12 22:50:06,750 botocore.hooks [DEBUG] Event needs-retry.s3.GetObject: calling handler <bound method S3RegionRedirectorv2.redirect_from_error of <botocore.utils.S3RegionRedirectorv2 object at 0x7aaddce57e50>>
LCFM_LCM-10_V100_2020_S60W030_MAP.tif   |████████████████████| 100% 5.15M/5.15MB

GeoServer 2.28.5 Log

geoserver-1  | 13 Oct 21:30:18 DEBUG  [interceptor.ExecutionInterceptorChain] - Interceptor 'software.amazon.awssdk.services.s3.endpoints.internal.S3RequestSetEndpointInterceptor@7a74c24b' modified the message with its modifyHttpRequest method.
geoserver-1  | 13 Oct 21:30:18 TRACE  [interceptor.ExecutionInterceptorChain] - Old: DefaultSdkHttpFullRequest(httpMethod=GET, protocol=https, host=dataspace.copernicus.eu, encodedPath=/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif, headers=[Range], queryParameters=[])
geoserver-1  | New: DefaultSdkHttpFullRequest(httpMethod=GET, protocol=https, host=eodata.dataspace.copernicus.eu, encodedPath=/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif, headers=[Range], queryParameters=[])
geoserver-1  | 13 Oct 21:30:18 DEBUG  [interceptor.ExecutionInterceptorChain] - Interceptor 'software.amazon.awssdk.services.s3.internal.handlers.EnableTrailingChecksumInterceptor@66f14809' modified the message with its modifyHttpRequest method.
geoserver-1  | 13 Oct 21:30:18 TRACE  [interceptor.ExecutionInterceptorChain] - Old: DefaultSdkHttpFullRequest(httpMethod=GET, protocol=https, host=eodata.dataspace.copernicus.eu, encodedPath=/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif, headers=[Range], queryParameters=[])
geoserver-1  | New: DefaultSdkHttpFullRequest(httpMethod=GET, protocol=https, host=eodata.dataspace.copernicus.eu, encodedPath=/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif, headers=[Range, x-amz-te], queryParameters=[])
geoserver-1  | 13 Oct 21:30:18 DEBUG  [retries.LegacyRetryStrategy] - Request attempt 1 token acquired (backoff: 0ms, cost: 0, capacity: 500/500)
geoserver-1  | 13 Oct 21:30:18 DEBUG  [awssdk.request] - Sending Request: DefaultSdkHttpFullRequest(httpMethod=GET, protocol=https, host=eodata.dataspace.copernicus.eu, encodedPath=/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif, headers=[amz-sdk-invocation-id, Range, User-Agent, x-amz-te], queryParameters=[])
geoserver-1  | 13 Oct 21:30:18 DEBUG  [stages.AsyncSigningStage] - Using SelectedAuthScheme: aws.auth#sigv4
geoserver-1  | 13 Oct 21:30:18 DEBUG  [signer.DefaultV4RequestSigner] - AWS4 Canonical Request: GET
geoserver-1  | /eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif
geoserver-1  |
geoserver-1  | amz-sdk-invocation-id:2c08bd33-7b42-d406-8dd0-fa5010432d88
geoserver-1  | amz-sdk-request:attempt=1; max=4
geoserver-1  | host:eodata.dataspace.copernicus.eu
geoserver-1  | range:bytes=0-16383
geoserver-1  | x-amz-content-sha256:UNSIGNED-PAYLOAD
geoserver-1  | x-amz-date:20261013T213018Z
geoserver-1  | x-amz-te:append-md5
geoserver-1  |
geoserver-1  | amz-sdk-invocation-id;amz-sdk-request;host;range;x-amz-content-sha256;x-amz-date;x-amz-te
geoserver-1  | UNSIGNED-PAYLOAD
geoserver-1  | 13 Oct 21:30:18 DEBUG  [signer.DefaultV4RequestSigner] - AWS4 Canonical Request Hash: 50c2b386b1ddc3802f965be454409b4a69adde5bd52dd93d80a3c3e47b94fb17
geoserver-1  | 13 Oct 21:30:18 DEBUG  [signer.DefaultV4RequestSigner] - AWS4 String to sign: AWS4-HMAC-SHA256
geoserver-1  | 20261013T213018Z
geoserver-1  | 20261013/us-east-1/s3/aws4_request
geoserver-1  | 50c2b386b1ddc3802f965be454409b4a69adde5bd52dd93d80a3c3e47b94fb17
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03] REGISTERED
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03] CONNECT: eodata.dataspace.copernicus.eu/45.92.243.127:443
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] ACTIVE
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] WRITE: software.amazon.awssdk.http.nio.netty.internal.NettyRequestExecutor$StreamedRequest(DefaultHttpRequest(decodeResult: success, version: HTTP/1.1)
geoserver-1  | GET /eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif HTTP/1.1
geoserver-1  | Host: eodata.dataspace.copernicus.eu
geoserver-1  | amz-sdk-invocation-id: 2c08bd33-7b42-d406-8dd0-fa5010432d88
geoserver-1  | amz-sdk-request: attempt=1; max=4
geoserver-1  | Authorization: AWS4-HMAC-SHA256 Credential=MYACCESSKEY/20261013/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;host;range;x-amz-content-sha256;x-amz-date;x-amz-te, Signature=12263fe79f3eb6fea2634c4be1da2eb65bf33c05290f9f145d009908f6aff2e0
geoserver-1  | Range: bytes=0-16383
geoserver-1  | User-Agent: aws-sdk-java/2.27.23 Linux/6.8.0-142-generic OpenJDK_64-Bit_Server_VM/21.0.11+10-LTS Java/21.0.11 vendor/Eclipse_Adoptium io/async http/NettyNio cfg/retry-mode/legacy
geoserver-1  | x-amz-content-sha256: UNSIGNED-PAYLOAD
geoserver-1  | X-Amz-Date: 20261013T213018Z
geoserver-1  | x-amz-te: append-md5)
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] FLUSH
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] FLUSH
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] WRITE, DefaultHttpContent(data: EmptyByteBufBE, decoderResult: success), 0B
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] FLUSH
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] READ COMPLETE
geoserver-1  | 13 Oct 21:30:18 DEBUG  [ssl.SslHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] HANDSHAKEN: protocol:TLSv1.3 cipher suite:TLS_AES_128_GCM_SHA256
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] USER_EVENT: SslHandshakeCompletionEvent(SUCCESS)
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] WRITE, EmptyLastHttpContent, 0B
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] FLUSH
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] READ COMPLETE
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] READ: DefaultHttpResponse(decodeResult: success, version: HTTP/1.1)
geoserver-1  | HTTP/1.1 403 Forbidden
geoserver-1  | content-type: application/xml
geoserver-1  | content-length: 420
geoserver-1  | date: Tue, 13 Oct 2026 21:30:18 GMT
geoserver-1  | server: envoy
geoserver-1  | 13 Oct 21:30:18 DEBUG  [awssdk.requestId] - Received failed response: 403, Request ID: not available, Extended Request ID: not available
geoserver-1  | 13 Oct 21:30:18 DEBUG  [awssdk.request] - Received failed response: 403, Request ID: not available, Extended Request ID: not available
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] READ: DefaultLastHttpContent(data: UnpooledSlicedByteBuf(ridx: 0, widx: 420, cap: 420/420, unwrapped: UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeDirectByteBuf(ridx: 420, widx: 420, cap: 442)), decoderResult: success), 420B
geoserver-1  |          +-------------------------------------------------+
geoserver-1  |          |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
geoserver-1  | +--------+-------------------------------------------------+----------------+
geoserver-1  | |00000000| 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 |<?xml version="1|
geoserver-1  | |00000010| 2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d 22 55 54 |.0" encoding="UT|
geoserver-1  | |00000020| 46 2d 38 22 3f 3e 3c 45 72 72 6f 72 3e 3c 43 6f |F-8"?><Error><Co|
geoserver-1  | |00000030| 64 65 3e 53 69 67 6e 61 74 75 72 65 44 6f 65 73 |de>SignatureDoes|
geoserver-1  | |00000040| 4e 6f 74 4d 61 74 63 68 3c 2f 43 6f 64 65 3e 3c |NotMatch</Code><|
geoserver-1  | |00000050| 4d 65 73 73 61 67 65 3e 54 68 65 20 72 65 71 75 |Message>The requ|
geoserver-1  | |00000060| 65 73 74 20 73 69 67 6e 61 74 75 72 65 20 77 65 |est signature we|
geoserver-1  | |00000070| 20 63 61 6c 63 75 6c 61 74 65 64 20 64 6f 65 73 | calculated does|
geoserver-1  | |00000080| 20 6e 6f 74 20 6d 61 74 63 68 20 74 68 65 20 73 | not match the s|
geoserver-1  | |00000090| 69 67 6e 61 74 75 72 65 20 79 6f 75 20 70 72 6f |ignature you pro|
geoserver-1  | |000000a0| 76 69 64 65 64 3c 2f 4d 65 73 73 61 67 65 3e 3c |vided</Message><|
geoserver-1  | |000000b0| 52 65 73 6f 75 72 63 65 3e 2f 65 6f 64 61 74 61 |Resource>/eodata|
geoserver-1  | |000000c0| 2f 43 4c 4d 53 2f 6c 61 6e 64 63 6f 76 65 72 5f |/CLMS/landcover_|
geoserver-1  | |000000d0| 6c 61 6e 64 75 73 65 2f 64 79 6e 61 6d 69 63 5f |landuse/dynamic_|
geoserver-1  | |000000e0| 6c 61 6e 64 5f 63 6f 76 65 72 2f 6c 63 6d 5f 67 |land_cover/lcm_g|
geoserver-1  | |000000f0| 6c 6f 62 61 6c 5f 31 30 6d 5f 79 65 61 72 6c 79 |lobal_10m_yearly|
geoserver-1  | |00000100| 5f 76 31 2f 32 30 32 30 2f 30 31 2f 30 31 2f 4c |_v1/2020/01/01/L|
geoserver-1  | |00000110| 43 46 4d 5f 4c 43 4d 2d 31 30 5f 56 31 30 30 5f |CFM_LCM-10_V100_|
geoserver-1  | |00000120| 32 30 32 30 5f 53 36 30 57 30 33 30 5f 63 6f 67 |2020_S60W030_cog|
geoserver-1  | |00000130| 2f 4c 43 46 4d 5f 4c 43 4d 2d 31 30 5f 56 31 30 |/LCFM_LCM-10_V10|
geoserver-1  | |00000140| 30 5f 32 30 32 30 5f 53 36 30 57 30 33 30 5f 4d |0_2020_S60W030_M|
geoserver-1  | |00000150| 41 50 2e 74 69 66 3c 2f 52 65 73 6f 75 72 63 65 |AP.tif</Resource|
geoserver-1  | |00000160| 3e 3c 52 65 71 75 65 73 74 49 64 3e 32 30 31 30 |><RequestId>2010|
geoserver-1  | |00000170| 63 39 35 37 2d 64 61 32 31 2d 39 33 30 38 2d 39 |c957-da21-9308-9|
geoserver-1  | |00000180| 32 63 66 2d 64 63 63 39 35 61 31 30 63 37 36 36 |2cf-dcc95a10c766|
geoserver-1  | |00000190| 3c 2f 52 65 71 75 65 73 74 49 64 3e 3c 2f 45 72 |</RequestId></Er|
geoserver-1  | |000001a0| 72 6f 72 3e                                     |ror>            |
geoserver-1  | +--------+-------------------------------------------------+----------------+
geoserver-1  | 13 Oct 21:30:18 DEBUG  [nrs.HttpStreamsHandler] - [Channel: [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443]] Received LastHttpContent [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] with ignoreBodyRead as false
geoserver-1  | 13 Oct 21:30:18 DEBUG  [logging.LoggingHandler] - [id: 0x2e0bed03, L:/192.168.55.111:37500 - R:eodata.dataspace.copernicus.eu/45.92.243.127:443] READ COMPLETE
geoserver-1  | 13 Oct 21:30:18 DEBUG  [retry.ClockSkew] - Reported service date: Tue, 13 Oct 2026 21:30:18 GMT
geoserver-1  | 13 Oct 21:30:18 TRACE  [internal.IdleConnectionCountingChannelPool] - Idle connection count incremented, now 1
geoserver-1  | 13 Oct 21:30:18 DEBUG  [retry.ClockSkew] - Reported service date: Tue, 13 Oct 2026 21:30:18 GMT
geoserver-1  | 13 Oct 21:30:18 DEBUG  [retries.LegacyRetryStrategy] - Request attempt 1 encountered non-retryable failure
geoserver-1  | software.amazon.awssdk.services.s3.model.S3Exception: The request signature we calculated does not match the signature you provided (Service: S3, Status Code: 403, Request ID: 2010c957-da21-9308-92cf-dcc95a10c766)

GeoServer 3.1 log is very similar to 2.28.5 but does not show the complete HTTP request and does not fit in this post. I have therefore left it out for now.

On a side note, access to a public S3 file via HTTP, such as https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/41/R/NJ/2024/10/S2B_41RNJ_20241030_0_L2A/TCI.tif, with the cog-http plugin and no credentials, works as expected.

Anonymous access via cog-s3 does not seem to be supported, and authenticated access to an S3 HTTP URL via cog-http fails, presumably because AWS and HTTP use different authentication methods, with:

Unable to read header for https://eodata.dataspace.copernicus.eu/eodata/CLMS/landcover_landuse/dynamic_land_cover/lcm_global_10m_yearly_v1/2020/01/01/LCFM_LCM-10_V100_2020_S60W030_cog/LCFM_LCM-10_V100_2020_S60W030_MAP.tif