diff --git a/fitsio/fitslib.py b/fitsio/fitslib.py
index 7ef718b..3d43549 100644
--- a/fitsio/fitslib.py
+++ b/fitsio/fitslib.py
@@ -25,7 +25,7 @@
 import numpy
 
 from . import _fitsio_wrap
-from .util import IS_PY3, mks, array_to_native, isstring
+from .util import IS_PY3, mks, array_to_native, isstring, copy_if_needed
 from .header import FITSHDR
 from .hdu import (
     ANY_HDU, IMAGE_HDU, BINARY_TBL, ASCII_TBL,
@@ -957,7 +957,7 @@ def create_image_hdu(self,
                 if IS_PY3 and img2send.dtype.char == 'U':
                     # for python3, we convert unicode to ascii
                     # this will error if the character is not in ascii
-                    img2send = img2send.astype('S', copy=False)
+                    img2send = img2send.astype('S', copy=copy_if_needed)
 
             else:
                 self._ensure_empty_image_ok()
@@ -1738,7 +1738,7 @@ def npy_obj2fits(data, name=None):
         else:
             fits_dtype = _table_npy2fits_form['S']
     else:
-        arr0 = numpy.array(first, copy=False)
+        arr0 = numpy.array(first, copy=copy_if_needed)
         dtype0 = arr0.dtype
         npy_dtype = dtype0.descr[0][1][1:]
         if npy_dtype[0] == 'S' or npy_dtype[0] == 'U':
diff --git a/fitsio/hdu/image.py b/fitsio/hdu/image.py
index 8c06bcf..a2d16c8 100644
--- a/fitsio/hdu/image.py
+++ b/fitsio/hdu/image.py
@@ -27,7 +27,7 @@
 
 from math import floor
 from .base import HDUBase, IMAGE_HDU
-from ..util import IS_PY3, array_to_native
+from ..util import IS_PY3, array_to_native, copy_if_needed
 
 # for python3 compat
 if IS_PY3:
@@ -146,7 +146,7 @@ def write(self, img, start=0, **keys):
         if IS_PY3 and img_send.dtype.char == 'U':
             # for python3, we convert unicode to ascii
             # this will error if the character is not in ascii
-            img_send = img_send.astype('S', copy=False)
+            img_send = img_send.astype('S', copy=copy_if_needed)
 
         if not numpy.isscalar(start):
             # convert to scalar offset
diff --git a/fitsio/hdu/table.py b/fitsio/hdu/table.py
index f210121..3b462b1 100644
--- a/fitsio/hdu/table.py
+++ b/fitsio/hdu/table.py
@@ -36,7 +36,8 @@
     array_to_native,
     array_to_native_c,
     FITSRuntimeWarning,
-    mks
+    mks,
+    copy_if_needed,
 )
 from .base import HDUBase, ASCII_TBL, IMAGE_HDU, _hdu_type_map
 
@@ -282,7 +283,7 @@ def write(self, data, firstrow=0, columns=None, names=None, slow=False,
                     if IS_PY3 and colref.dtype.char == 'U':
                         # for python3, we convert unicode to ascii
                         # this will error if the character is not in ascii
-                        colref = colref.astype('S', copy=False)
+                        colref = colref.astype('S', copy=copy_if_needed)
 
                     nonobj_arrays.append(colref)
 
@@ -347,7 +348,7 @@ def write_column(self, column, data, firstrow=0, **keys):
         if IS_PY3 and data_send.dtype.char == 'U':
             # for python3, we convert unicode to ascii
             # this will error if the character is not in ascii
-            data_send = data_send.astype('S', copy=False)
+            data_send = data_send.astype('S', copy=copy_if_needed)
 
         self._verify_column_data(colnum, data_send)
 
@@ -1420,13 +1421,13 @@ def _extract_rows(self, rows, sort=False):
         Extract an array of rows from an input scalar or sequence
         """
         if rows is not None:
-            rows = np.array(rows, ndmin=1, copy=False, dtype='i8')
+            rows = np.array(rows, ndmin=1, copy=copy_if_needed, dtype='i8')
             if sort:
                 rows = np.unique(rows)
                 return rows, None
 
             # returns unique, sorted.  Force i8 for 32-bit systems
-            sortind = np.array(rows.argsort(), dtype='i8', copy=False)
+            sortind = np.array(rows.argsort(), dtype='i8', copy=copy_if_needed)
 
             maxrow = self._info['nrows']-1
             if rows.size > 0:
@@ -1583,7 +1584,7 @@ def _maybe_decode_fits_ascii_strings_to_unicode_py3(self, array):
                 else:
                     new_dt.append(_dt)
             if do_conversion:
-                array = array.astype(new_dt, copy=False)
+                array = array.astype(new_dt, copy=copy_if_needed)
         return array
 
     def _convert_bool_array(self, array):
@@ -1721,7 +1722,7 @@ def _read_var_column(self, colnum, rows, sortind, vstorage):
                 descr = 'S%d' % max_size
                 array = np.fromiter(dlist, descr)
                 if IS_PY3:
-                    array = array.astype('U', copy=False)
+                    array = array.astype('U', copy=copy_if_needed)
             else:
                 descr = dlist[0].dtype.str
                 array = np.zeros((len(dlist), max_size), dtype=descr)
diff --git a/fitsio/util.py b/fitsio/util.py
index 562b609..5d337be 100644
--- a/fitsio/util.py
+++ b/fitsio/util.py
@@ -41,7 +41,10 @@ def cfitsio_version(asfloat=False):
             numpy.uint64, numpy.int64)
 
 # different for py3
-_stypes += (numpy.string_, numpy.str_)
+if numpy.lib.NumpyVersion(numpy.__version__) < "1.28.0":
+    _stypes += (numpy.string_, numpy.str_,)
+else:
+    _stypes += (numpy.bytes_, numpy.str_,)
 
 # for header keywords
 _ftypes = (float, numpy.float32, numpy.float64)
@@ -134,9 +137,22 @@ def array_to_native(array, inplace=False):
     return output
 
 
+if numpy.lib.NumpyVersion(numpy.__version__) >= "2.0.0":
+    copy_if_needed = None
+elif numpy.lib.NumpyVersion(numpy.__version__) < "1.28.0":
+    copy_if_needed = False
+else:
+    # 2.0.0 dev versions, handle cases where copy may or may not exist
+    try:
+        numpy.array([1]).__array__(copy=None)
+        copy_if_needed = None
+    except TypeError:
+        copy_if_needed = False
+
+
 def array_to_native_c(array_in, inplace=False):
     # copy only made if not C order
-    arr = numpy.array(array_in, order='C', copy=False)
+    arr = numpy.array(array_in, order='C', copy=copy_if_needed)
     return array_to_native(arr, inplace=inplace)
 
 
