diff --git a/packages/design-system/src/components/OcDatepicker/OcDatepicker.vue b/packages/design-system/src/components/OcDatepicker/OcDatepicker.vue
index c0bf6951134..b0f386119b2 100644
--- a/packages/design-system/src/components/OcDatepicker/OcDatepicker.vue
+++ b/packages/design-system/src/components/OcDatepicker/OcDatepicker.vue
@@ -5,6 +5,7 @@
     :label="label"
     type="date"
     :min="minDate?.toISODate()"
+    :max="maxDate?.toISODate()"
     :fix-message-line="true"
     :error-message="errorMessage"
     :clear-button-enabled="isClearable"
@@ -25,7 +26,8 @@ export default defineComponent({
     label: { type: String, required: true },
     isClearable: { type: Boolean, default: true },
     currentDate: { type: Object as PropType<DateTime>, required: false, default: null },
-    minDate: { type: Object as PropType<DateTime>, required: false, default: null }
+    minDate: { type: Object as PropType<DateTime>, required: false, default: null },
+    maxDate: { type: Object as PropType<DateTime>, required: false, default: null }
   },
   emits: ['dateChanged'],
   setup(props, { emit }) {
@@ -44,6 +46,13 @@ export default defineComponent({
       return unref(date) < props.minDate
     })
 
+    const isMaxDateExceeded = computed(() => {
+      if (!props.maxDate || !unref(date)) {
+        return false
+      }
+      return unref(date) > props.maxDate
+    })
+
     const errorMessage = computed(() => {
       if (unref(isMinDateUndercut)) {
         return $gettext('The date must be after %{date}', {
@@ -53,6 +62,14 @@ export default defineComponent({
             .toLocaleString(DateTime.DATE_SHORT)
         })
       }
+      if (unref(isMaxDateExceeded)) {
+        return $gettext('The date must be before %{date}', {
+          date: props.maxDate
+            .plus({ day: 1 })
+            .setLocale(current)
+            .toLocaleString(DateTime.DATE_SHORT)
+        })
+      }
       return ''
     })
 
@@ -73,6 +90,7 @@ export default defineComponent({
       date,
       () => {
         emit('dateChanged', { date: unref(date), error: unref(isMinDateUndercut) })
+        emit('dateChanged', { date: unref(date), error: unref(isMaxDateExceeded) })
       },
       {
         deep: true
@@ -97,7 +115,7 @@ export default defineComponent({
 ```js
 <template>
   <div>
-    <oc-datepicker :current-date="currentDate" :min-date="minDate" label="Enter or pick a date"
+    <oc-datepicker :current-date="currentDate" :min-date="minDate" :max-date="maxDate" label="Enter or pick a date"
                    @date-changed="onDateChanged"/>
     <p v-if="selectedDate" v-text="selectedDate"/>
   </div>
@@ -107,7 +125,7 @@ export default defineComponent({
 
   export default {
     data: () => ({
-      minDate: DateTime.now(), currentDate: DateTime.now(), selectedDate: ''
+      minDate: DateTime.now(), currentDate: DateTime.now(), selectedDate: '', maxDate: null
     }),
     methods: {
       onDateChanged({date}) {
diff --git a/packages/web-app-files/src/components/Modals/DatePickerModal.vue b/packages/web-app-files/src/components/Modals/DatePickerModal.vue
index 4ace3be2e6f..10257d13369 100644
--- a/packages/web-app-files/src/components/Modals/DatePickerModal.vue
+++ b/packages/web-app-files/src/components/Modals/DatePickerModal.vue
@@ -3,6 +3,7 @@
     :label="$gettext('Expiration date')"
     type="date"
     :min-date="minDate"
+    :max-date="maxDate"
     :current-date="currentDate"
     :is-clearable="isClearable"
     @date-changed="onDateChanged"
@@ -38,6 +39,7 @@ export default defineComponent({
     modal: { type: Object as PropType<Modal>, required: true },
     currentDate: { type: Object as PropType<DateTime>, required: false, default: null },
     minDate: { type: Object as PropType<DateTime>, required: false, default: null },
+    maxDate: { type: Object as PropType<DateTime>, required: false, default: null },
     isClearable: { type: Boolean, default: true }
   },
   emits: ['confirm', 'cancel'],
diff --git a/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue b/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue
index b0fb6018cfd..d0eb2314c89 100644
--- a/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue
+++ b/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue
@@ -92,7 +92,8 @@ import {
   useResourcesStore,
   useLinkTypes,
   useCanShare,
-  UpdateLinkOptions
+  UpdateLinkOptions,
+  useCapabilityStore
 } from '@ownclouders/web-pkg'
 import { shareViaLinkHelp, shareViaIndirectLinkHelp } from '../../../helpers/contextualHelpers'
 import { isSpaceResource, LinkShare } from '@ownclouders/web-client'
@@ -102,11 +103,14 @@ import { isLocationSharesActive, useSharesStore } from '@ownclouders/web-pkg'
 import { useGettext } from 'vue3-gettext'
 import { storeToRefs } from 'pinia'
 import { SharingLinkType } from '@ownclouders/web-client/graph/generated'
+import { DateTime, Duration } from 'luxon'
 
 export default defineComponent({
   name: 'FileLinks',
   components: { ListItem },
   setup() {
+    const { sharingPublicExpireDateDefaultRWFolders, sharingPublicExpireDateMaxRWFolders } =
+      useCapabilityStore()
     const { showMessage, showErrorMessage } = useMessages()
     const { $gettext } = useGettext()
     const ability = useAbility()
@@ -124,12 +128,15 @@ export default defineComponent({
       return canShare({ space: unref(space), resource: unref(resource) })
     })
 
+    const language = useGettext()
+
     const sharesStore = useSharesStore()
     const { updateLink, deleteLink } = sharesStore
     const { linkShares } = storeToRefs(sharesStore)
 
     const configStore = useConfigStore()
     const { options: configOptions } = storeToRefs(configStore)
+    const alertRwFolders = configStore.options.alertRwFolders
 
     const { actions: createLinkActions } = useFileActionsCreateLink()
     const createLinkAction = computed<FileAction>(() =>
@@ -192,14 +199,62 @@ export default defineComponent({
       options: UpdateLinkOptions['options']
     }) => {
       try {
+        if (
+          options.type === 'edit' &&
+          unref(resource).isFolder &&
+          !linkShare.expirationDateTime &&
+          sharingPublicExpireDateDefaultRWFolders
+        ) {
+          Object.assign(options, {
+            ...options,
+            expirationDateTime: DateTime.now()
+              .plus(sharingPublicExpireDateDefaultRWFolders)
+              .endOf('day')
+              .toISO()
+          })
+        }
+        if (
+          unref(resource).isFolder &&
+          linkShare.expirationDateTime &&
+          sharingPublicExpireDateDefaultRWFolders &&
+          sharingPublicExpireDateMaxRWFolders
+        ) {
+          if (
+            DateTime.fromISO(linkShare.expirationDateTime).diff(DateTime.now(), 'days').as('days') >
+            Duration.fromObject(sharingPublicExpireDateMaxRWFolders).as('days')
+          ) {
+            Object.assign(options, {
+              ...options,
+              expirationDateTime: DateTime.now()
+                .plus(sharingPublicExpireDateDefaultRWFolders)
+                .endOf('day')
+                .toISO()
+            })
+          }
+        }
         await updateLink({
           clientService,
           space: unref(space),
           resource: unref(resource),
           linkShare,
           options
+        }).then(() => {
+          showMessage({ title: $gettext('Link was updated successfully') })
         })
-        showMessage({ title: $gettext('Link was updated successfully') })
+        if (options.type === 'edit' && unref(resource).isFolder && alertRwFolders) {
+          if (!document.getElementById('files-file-link-warning')) {
+            const warningMessage = document.createElement('div')
+            warningMessage.className = 'oc-mb-m oc-p-s oc-background-secondary oc-rounded'
+            warningMessage.id = 'files-file-link-warning'
+            warningMessage.innerHTML = $gettext(
+              alertRwFolders[language.current] ?? alertRwFolders[Object.keys(alertRwFolders)[0]]
+            )
+            document.getElementById('files-links-list').parentElement.prepend(warningMessage)
+            setTimeout(() => {
+              warningMessage.remove()
+            }, 10000)
+          }
+        }
       } catch (e) {
         console.error(e)
         showErrorMessage({
@@ -349,4 +404,9 @@ export default defineComponent({
     margin-top: var(--oc-space-medium);
   }
 }
+#files-file-link-warning {
+  color: var(--oc-color-swatch-danger-default);
+  text-align: center;
+  border: solid 1px var(--oc-color-swatch-danger-muted);
+}
 </style>
diff --git a/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue b/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue
index d4378ea62e1..541bcd79da1 100644
--- a/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue
+++ b/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue
@@ -46,6 +46,7 @@
 import { DateTime } from 'luxon'
 import {
   createLocationSpaces,
+  useCapabilityStore,
   useGetMatchingSpace,
   useModals,
   useResourcesStore
@@ -97,6 +98,7 @@ export default defineComponent({
     const { $gettext } = useGettext()
     const { getMatchingSpace } = useGetMatchingSpace()
     const resourcesStore = useResourcesStore()
+    const { sharingPublicExpireDateMaxRWFolders } = useCapabilityStore()
     const editPublicLinkDropdown = useTemplateRef<typeof OcDrop>('editPublicLinkDropdown')
 
     const resource = inject<Ref<Resource>>('resource')
@@ -110,7 +112,13 @@ export default defineComponent({
         customComponent: DatePickerModal,
         customComponentAttrs: () => ({
           currentDate: currentDate.isValid ? currentDate : null,
-          minDate: DateTime.now()
+          minDate: DateTime.now(),
+          maxDate:
+            resource.value.isFolder &&
+            props.linkShare.type === SharingLinkType.Edit &&
+            sharingPublicExpireDateMaxRWFolders
+              ? DateTime.now().plus(sharingPublicExpireDateMaxRWFolders).endOf('day')
+              : null
         }),
         onConfirm: (expirationDateTime: DateTime) => {
           emit('updateLink', {
@@ -214,18 +222,27 @@ export default defineComponent({
           method: showDatePickerModal
         })
 
-        result.push({
-          id: 'remove-expiration',
-          title: $gettext('Remove expiration date'),
-          icon: 'calendar-close',
-          method: () => {
-            emit('updateLink', {
-              linkShare: { ...props.linkShare },
-              options: { expirationDateTime: null }
-            })
-            unref(editPublicLinkDropdown).hide()
-          }
-        })
+        // only if it isn't a edit folder link
+        if (
+          !(
+            props.linkShare.type === SharingLinkType.Edit &&
+            resource.value.isFolder &&
+            sharingPublicExpireDateMaxRWFolders
+          )
+        ) {
+          result.push({
+            id: 'remove-expiration',
+            title: $gettext('Remove expiration date'),
+            icon: 'calendar-close',
+            method: () => {
+              emit('updateLink', {
+                linkShare: { ...props.linkShare },
+                options: { expirationDateTime: null }
+              })
+              unref(editPublicLinkDropdown).hide()
+            }
+          })
+        }
       } else if (!unref(isInternalLink)) {
         result.push({
           id: 'add-expiration',
diff --git a/packages/web-client/src/ocs/capabilities.ts b/packages/web-client/src/ocs/capabilities.ts
index 57408cbef90..e1588311020 100644
--- a/packages/web-client/src/ocs/capabilities.ts
+++ b/packages/web-client/src/ocs/capabilities.ts
@@ -145,6 +145,19 @@ export interface Capabilities {
         send_mail?: boolean
         supports_upload_only?: boolean
         upload?: boolean
+        expire_date?: {
+          enabled?: boolean
+          default_rw_folders?: {
+            years?: number
+            months?: number
+            days?: number
+          }
+          max_rw_folders?: {
+            years?: number
+            months?: number
+            days?: number
+          }
+        }
       }
       search_min_length?: number
       user?: {
diff --git a/packages/web-pkg/src/components/CreateLinkModal.vue b/packages/web-pkg/src/components/CreateLinkModal.vue
index eab55ee9aef..6c01302f0a1 100644
--- a/packages/web-pkg/src/components/CreateLinkModal.vue
+++ b/packages/web-pkg/src/components/CreateLinkModal.vue
@@ -48,6 +48,11 @@
       v-if="isAdvancedMode"
       class="oc-mt-s"
       :min-date="DateTime.now()"
+      :max-date="
+        isFolder && selectedType === 'edit' && sharingPublicExpireDateMaxRWFolders
+          ? DateTime.now().plus(sharingPublicExpireDateMaxRWFolders).endOf('day')
+          : null
+      "
       :label="$gettext('Expiry date')"
       @date-changed="onExpiryDateChanged"
     />
@@ -69,7 +74,7 @@
         appearance="filled"
         variation="primary"
         :disabled="confirmButtonDisabled"
-        @click="$emit('confirm')"
+        @click="$emit('confirm', { isRW: selectedType === 'edit', isFolder })"
         >{{ confirmButtonText }}
       </oc-button>
       <oc-button
@@ -94,7 +99,13 @@
             <oc-button
               class="oc-modal-body-actions-confirm-password action-menu-item"
               appearance="raw"
-              @click="$emit('confirm', { copyPassword: true })"
+              @click="
+                $emit('confirm', {
+                  copyPassword: true,
+                  isRW: selectedType === 'edit',
+                  isFolder
+                })
+              "
               >{{ $gettext('Copy link and password') }}
             </oc-button>
           </li>
@@ -125,7 +136,8 @@ import {
   useLinkTypes,
   Modal,
   useSharesStore,
-  useClientService
+  useClientService,
+  useCapabilityStore
 } from '../composables'
 import { LinkShare, SpaceResource } from '@ownclouders/web-client'
 import { Resource } from '@ownclouders/web-client'
@@ -169,6 +181,7 @@ export default defineComponent({
     const { addLink } = useSharesStore()
     const isAdvancedMode = ref(false)
     const isInvalidExpiryDate = ref(false)
+    const { sharingPublicExpireDateMaxRWFolders } = useCapabilityStore()
 
     const isFolder = computed(() => props.resources.every(({ isFolder }) => isFolder))
 
@@ -297,6 +310,15 @@ export default defineComponent({
 
     const updateSelectedLinkType = (type: SharingLinkType) => {
       selectedType.value = type
+      onExpiryDateChanged({
+        date: unref(selectedExpiry),
+        error:
+          sharingPublicExpireDateMaxRWFolders &&
+          unref(selectedExpiry)?.toISO() >
+            DateTime.now().plus(sharingPublicExpireDateMaxRWFolders).endOf('day').toISO() &&
+          isFolder &&
+          unref(selectedType) === 'edit'
+      })
     }
 
     onMounted(() => {
@@ -332,7 +354,9 @@ export default defineComponent({
       setAdvancedMode,
       onExpiryDateChanged,
       confirmButtonDisabled,
+      isFolder,
       DateTime,
+      sharingPublicExpireDateMaxRWFolders,
 
       // unit tests
       onConfirm
diff --git a/packages/web-pkg/src/composables/actions/files/useFileActionsCreateLink.ts b/packages/web-pkg/src/composables/actions/files/useFileActionsCreateLink.ts
index 1dc5e33932a..7ac5a6558ab 100644
--- a/packages/web-pkg/src/composables/actions/files/useFileActionsCreateLink.ts
+++ b/packages/web-pkg/src/composables/actions/files/useFileActionsCreateLink.ts
@@ -11,7 +11,8 @@ import {
   useModals,
   useUserStore,
   useCapabilityStore,
-  useSharesStore
+  useSharesStore,
+  useConfigStore
 } from '../../piniaStores'
 import { useClipboard } from '../../clipboard'
 import { useClientService } from '../../clientService'
@@ -32,6 +33,7 @@ export const useFileActionsCreateLink = ({
   const { addLink } = useSharesStore()
   const { dispatchModal } = useModals()
   const { copyToClipboard } = useClipboard()
+  const configStore = useConfigStore()
 
   const proceedResult = async ({
     result,
@@ -40,7 +42,7 @@ export const useFileActionsCreateLink = ({
   }: {
     result: PromiseSettledResult<LinkShare>[]
     password?: string
-    options?: { copyPassword?: boolean }
+    options?: { isRW?: boolean; isFolder?: boolean; copyPassword?: boolean }
   }) => {
     const succeeded = result.filter(
       (val): val is PromiseFulfilledResult<LinkShare> => val.status === 'fulfilled'
@@ -70,6 +72,20 @@ export const useFileActionsCreateLink = ({
         }
       }
 
+      const language = useGettext()
+      const alertRwFolders = configStore.options.alertRwFolders
+
+      if (options.isRW && options.isFolder && alertRwFolders) {
+        dispatchModal({
+          variation: 'warning',
+          title: 'Default expiration date',
+          message: $gettext(
+            alertRwFolders[language.current] ?? alertRwFolders[Object.keys(alertRwFolders)[0]]
+          ),
+          confirmText: 'Got it'
+        })
+      }
+
       showMessage({
         title: $ngettext(successMessage, 'Links have been created successfully.', succeeded.length)
       })
diff --git a/packages/web-pkg/src/composables/piniaStores/capabilities.ts b/packages/web-pkg/src/composables/piniaStores/capabilities.ts
index 9aab623ef61..0ba4db19c62 100644
--- a/packages/web-pkg/src/composables/piniaStores/capabilities.ts
+++ b/packages/web-pkg/src/composables/piniaStores/capabilities.ts
@@ -35,6 +35,11 @@ const defaultValues = {
       enabled: true,
       password: {
         enforced_for: { read_only: false, upload_only: false, read_write: false }
+      },
+      expire_date: {
+        enabled: true,
+        default_rw_folders: null,
+        max_rw_folders: null
       }
     }
   },
@@ -118,6 +123,13 @@ export const useCapabilityStore = defineStore('capabilities', () => {
   const sharingPublicPasswordEnforcedFor = computed(
     () => unref(capabilities).files_sharing.public?.password.enforced_for
   )
+  const sharingPublicExpireDateDefaultRWFolders = computed(
+    () => unref(capabilities).files_sharing.public?.expire_date.default_rw_folders
+  )
+  const sharingPublicExpireDateMaxRWFolders = computed(
+    () => unref(capabilities).files_sharing.public?.expire_date.max_rw_folders
+  )
+
   const sharingSearchMinLength = computed(() => unref(capabilities).files_sharing.search_min_length)
   const sharingUserProfilePicture = computed(
     () => unref(capabilities).files_sharing.user?.profile_picture
@@ -175,6 +187,8 @@ export const useCapabilityStore = defineStore('capabilities', () => {
     sharingPublicAlias,
     sharingPublicDefaultPermissions,
     sharingPublicPasswordEnforcedFor,
+    sharingPublicExpireDateDefaultRWFolders,
+    sharingPublicExpireDateMaxRWFolders,
     sharingSearchMinLength,
     sharingUserProfilePicture,
     tusMaxChunkSize,
diff --git a/packages/web-pkg/src/composables/piniaStores/config/config.ts b/packages/web-pkg/src/composables/piniaStores/config/config.ts
index 5511545eb9a..dced5c611d6 100644
--- a/packages/web-pkg/src/composables/piniaStores/config/config.ts
+++ b/packages/web-pkg/src/composables/piniaStores/config/config.ts
@@ -37,7 +37,8 @@ const defaultOptions = {
   runningOnEos: false,
   tokenStorageLocal: true,
   userListRequiresFilter: false,
-  hideLogo: false
+  hideLogo: false,
+  alertRwFolders: {}
 } satisfies Partial<OptionsConfig>
 
 export const useConfigStore = defineStore('config', () => {
diff --git a/packages/web-pkg/src/composables/piniaStores/config/types.ts b/packages/web-pkg/src/composables/piniaStores/config/types.ts
index 8e8fc808c00..c7f5e33025b 100644
--- a/packages/web-pkg/src/composables/piniaStores/config/types.ts
+++ b/packages/web-pkg/src/composables/piniaStores/config/types.ts
@@ -126,7 +126,8 @@ const OptionsConfigSchema = z.object({
   hideAppSwitcher: z.boolean().optional(),
   hideAccountMenu: z.boolean().optional(),
   hideNavigation: z.boolean().optional(),
-  defaultLanguage: z.string().optional()
+  defaultLanguage: z.string().optional(),
+  alertRwFolders: z.object({}).optional()
 })
 
 export type OptionsConfig = z.infer<typeof OptionsConfigSchema>